Текст
                    В.Яншин, Г.Калинин
ОБРАБОТКА
ИЗОБРАЖЕНИЙ
НА ЯЗЫКЕ
ДЛЯ
алгоритмы
и программы
Издательство «Мир»


В. Яншин, Г Калинин алгоритмы и программы ква «МИР» 1994
ББК 32.97 Ябб УДК 681.3 Яншин В. В., Калинин Г. А. Я66 Обработка изображений на языке Си для IBM PC: Алго- Алгоритмы и программы. — М.: Мир, 1994. — с, ил. ISBN 5-03-002891-9 В книге приведены алгоритмы и тексты программ на языке Си ддя IBM PC по основным направлениям современной обра- ботки изображений. К каждой из программ даются коммента- комментарии, позволяющие легко трансформировать их для отечественных ПЭВМ типа СМ и ДВК. При отборе предлагаемых алгоритмов авторы ориентировались прежде всего на задачи научного и ис- исследовательского характера, связанные с анализом бинарных и многоуровневых монохромных изображений. Сюда входят под- подпрограммы ввода/вывода и компактного представления изображе- изображений, подпрограммы исследования основных свойств изображений (гистограммы, числовые характеристики), подпрограммы яр кост- костных и геометрических преобразований. Для научных работников, инженеров, аспирантов и студентов, специализирующихся в области обработки изображений. Я 2404090000-039 041@1)-94 КБ-11-93-33 ББК 32.97 Редакция литературы по информатике Научное издание Валерий Владимирович Яншин Григорий Александрович Калинин Обработка изображений на языке Си для IBM PC: Алгоритмы и программы Заведующий редакцией Э« Н. Бадиков. Ведущий редактор И. К. Короткова. Художник С. А. Бычков. Художественный редактор Н. М. Иванов |. Технический редактор О. Г. Лапко. Корректор С. А. Денисе»! Оригинал-макет подготовлен С. А. Янковой в пакете Т?Х с ист кириллических шрифтов, разработанных в редакции АИП ИБХ» 8265 Лицензия Л. Р К* 010174 от 22.01.92 г. Подписано к печати 1.11.93. Формат 60 х 88/16. Бумага офсетная. Объем 7,50 бум. л. Усл.-печ. л. 14,70. Усл. кр.- Уч.-изд. л. 12,65. Изд. К» 6/9001. Тираж 5 000 экз. ЗаказЗ* Издательство «Мир» Министерства печати и информации Россииci 129820, ГСП, Москва, И-110, 1-й Рижский пер., 2. Московская типография № 9 Министерства печати и информации Российской Федерации. 109033, Москва, Волочаевская ул., 40 ISBN 5-03-002891-9 Яншин 1994 к, Калинин Г. А.,
ПРЕДИСЛОВИЕ Цифровая обработка изображений — тема поистине необъят- необъятная и неисчерпаемая. К настоящему времени накоплена обширная литература, посвя- посвященная различным аспектам цифровой обработки полутоновых и бинарных изображений. Однако доминируют в ней работы, имею- имеющие ярко выраженную теоретическую направленность. Книги, посвященные описанию вычислительных алгоритмов об- обработки изображений, моментально становятся библиографической редкостью [1]. Единичные публикации, содержащие тексты про- программ [1-3], относятся, как правило, к области компьютерной (ин- (инженерной) графики. Описания лее известных графических пакетов прикладных программ обычно не содержат исходных текстов. Предлагаемая книга имеет практическую направленность. Она призвана в определенной мере заполнить существующий пробел, предоставив в распоряжение специалистов самой широкой ориен- ориентации некоторый базовый набор алгоритмов обработки изображе- изображений, запрограммированных на языке Си. Ограниченный объем книги не позволяет подробно изложить математические основы алгоритмов и дать детальное описание про- программ далее для какого-либо одного из направлений цифровой об- обработки изображений. Авторы ориентировались прежде всего на задачи научного и исследовательского характера, возникающие на этапе предварительной обработки [1-9]. Несомненно, что наличие готовых подпрограмм облегчит разработку проблемно-ориентиро- проблемно-ориентированных программных средств, позволяя сконцентрировать усилия на их нестандартной части. С другой стороны, использование неза- независимо работающими программистами унифицированных модулей позволит снять многие проблемы, возникающие при обмене про- программами и данными. Настоящая книга является первой в задуманной серии, посвя- посвященной вопросам, встающим при практической реализации различ- различных методов цифровой обработки изображений. Предполагается, что наряду с данной в серию войдут книги по кодированию и сжа- сжатию изображений, по сегментации изображений, по распознаванию образов, по визуальным реляционным базам данных и визуальным базам знаний, а также хниги по описанию и пониманию изобра- изображений. Серия будет выходить под редакцией доктора технических наук В. В. Яншина. Идея написания книги и выбор тематики при- принадлежат В. В. Яншину, им также изложены принципы построения алгоритмов. Составление, отладка и описание программ выполнены Г. А. Калининым. Авторы
ВВЕДЕНИЕ Мы предполагаем, что наш читатель знаком с цифровой вычисли- вычислительной техникой вообще и с персональным компьютером IBM PC в частности и располагает необходимой справочной литературой (например, распространенным руководством по общесистемным во- вопросам [10]). Предполагаются также знакомство с языком програм- программирования Си [11-13] и наличие некоторого опыта практической работы. Описанные в книге программы отлаживались в системе Турбо- Си; по работе в этой интегрированной среде имеется доступная литература [14, 15]. Большинство программ работоспособно также в среде Квик-Си. Все описанные алгоритмы сопровождаются текстами соответ- соответствующих подпрограмм на языке Си. Рядом с ними помещены также тексты демонстрационных программ. Они дают примеры использования подпрограмм, позволяют наблюдать их работу в раз- различны х режимах. Язык Си для обработки изображений Выбор языка Си обусловлен тем, что по своим возможностям он ма- мало б чем уступает Ассемблеру, являясь в то же время языком высо- высокого уровня со всеми вытекающими отсюда достоинствами. Кроме того, язык Си достаточно распространен, имеются и широко из- известны эффективные средства поддержки, такие, как интегриро- интегрированные среды Турбо-Си, Квик-Си и другие. Немаловажным достоинством Си является хорошая переноси- переносимость написанных на нем программ с одной вычислительной систе- системы на другую. Так, описанные в данной книге программы предна- предназначены для IBM-совместимых персональных компьютеров, однако большинство из них транслируются и правильно работают также и на DEC-соБместимых машинах. Необходимые изменения мини- минимальны ш касаются в основном головных файлов. Исключение со- составляют машинозависимые программы графического вывода на экран. Язык Си невелик по объему, лаконичен, однако обладает бога- богатыми выразительными возможностями. Ориентированность на со- создание структурированных программ обеспечивает высокую произ- производительность труда владеющего им программиста. Недостатки языка являются, как говорится, продолжением его достоинств. Так, лаконичность может обернуться трудночитаемы- трудночитаемыми программами. Богатство выразительных возможностей поро- порождает сложность освоения языка; кроме того практически любой
Введение 7 алгоритм может быть запрограммирован на Си несколькими спосо- способами. Выбор варианта — постоянная головная боль программиста, и он (выбор) во многом зависит от личных вкусов и пристрастий. При написании программ мы стремились нейтрализовать недо- недостатки, применяя, по возможности, наиболее простые, или, как те- теперь часто говорят, «прозрачные» конструкции. Поэтому некото- некоторые возможности языка не использованы вовсе, другие применя- применялись лишь эпизодически. Недостаточно высокую наглядность про- программ, написанных на Си, мы постарались компенсировать соблю- соблюдением правил хорошего тона: наличием отступов, пустых строк и комментариев в тексте программ. По нашему замыслу все эти меры призваны способствовать пониманию программ, в том числе и непрофессиональными программистами. Сделаем одно замечание по терминологии. В языке Си вме- вместо слов «программа» и «подпрограмма» принято говорить «функ- «функция». Это связано с тем, что в определенном смысле программы и подпрограммы в Си равноправны: и те и другие могут иметь параметры и возвращать (или не возвращать) значение и даже вы- вызывать друг друга! Общий термин «функция» для тех и других подчеркивает это равноправие. Однако как только изложение пе- перестает ограничиваться внутренней проблематикой языка, нередко ощущается заметное неудобство. Термин «функция» и без того несет значительную смысловую нагрузку, являясь общеупотребительным в математике. Поэтому мы здесь отходим от принятой в руководствах по Си терминоло- терминологии и, как правило, называем подпрограмму подпрограммой, а не функцией, сохраняя за последним термином его обычный смысл. Отнюдь не исключая из нашего словаря слово «функция» в при- принятом на Си смысле, мы употребляем его в этом значении только там, где не говорится о математических функциях и исключена возможность путаницы. О технике работы с пакетом программ Программы описываемого пакета хранятся во множестве файлов. Каждый файл содержит одну или несколько программных единиц (т. е. программ и подпрограмм). Файлы компилируются отдельно, независимо друг от друга. Вы знаете, что для успешной линковки (сборки) программ, включающих независимо скомпилированные функции, необходимо создать так называемый проектный файл, в котором перечисляют- перечисляются необходимые для сборки файлы. Создать его совсем несложно, и вы, конечно же, умеете это делать. Однако при большом количестве функций, т. е. именно в нашем случае, работать становится неудоб- неудобно, так как необходимо из всего множества функций отобрать лишь те, которые вызываются в данной программе (не забыв про вызовы из подпрограмм). Можно, конечно, поступить по принципу «кашу
8 Введение маслом не испортишь* и включать в каждый проектный файл все имеющиеся в пакете файлы подпрограмм. Не будем объяснять, что это далеко не лучшее решение. Кардинальным решением проблемы является использование би- библиотеки объектных модулей. Бели в библиотеку помещены все подпрограммы пакета, то в проектном модуле достаточно указать только два файла: главную программу и библиотеку. Обычно в библиотеку помещают функции, отладка которых завершена. При этом в проектном файле помимо главной программы и библиотеки остаются только модули, находящиеся «в работе» и подвергающи- подвергающиеся частым изменениям. Как поступить, если требуется внести изменения в подпрограм- подпрограмму, уже помещенную в библиотеку? Недостаточно просто перетран- перетранслировать исправленный файл, необходимо внести в библиотеку но- новый OBJ-файл взамен старого. Мы обычно откладываем реформу библиотеки до тех пор, пока не закончим отладку; а при отлад- отладке включаем имя исправляемого файла в проектный файл. При сборке в этом случае подсоединяется не библиотечная, а автоном- автономно оттранслированная функция. Для того чтобы в этом убедиться, полезно вставить в текст исправленной функции оператор печати, выдающий характерное сообщение. Когда работа по составлению и отладке подпрограмм завершена, библиотека приобретает законченный вид и перестает подвергаться изменениям. Попытайтесь сделать так, чтобы библиотека подклю- подключалась автоматически. Тогда можно будет вообще обходиться без проектного файла. Как создать библиотеку объектных модулей? Это делается с по- помощью утилиты tiib, входящей в комплект поставки Турбо-Си. Сна- Сначала необходимо оттранслировать все модули, получив объектные файлы. Такие файлы шмеют расширение obj. Затем надо набрать следующую команду: \tc\biiAtlib fcimag $ki№ag.gen,ki»ag.l@t Что означает такая запись? В левой части (до первого пробела) указано, что надо запустить программу tlib, иажодящуюся в под- поддиректории Ьт директория Хс (в вашей системе, возможно, путь поиска будет другим). Средняя часть команды задает имя создава- создаваемой библиотеки — kimag.iib. Расширение lib добавляется системой по умолчанию. В правой части команды указывается файл подста- подстановок kimag gen, который будет рассмотрен ниже, и файл kimag.lst, в который помещается оглавление вновь созданной библиотеки. Исходные OBJ-файлы и файл подстановок должны находиться в текущем директория. Туда же записываются результаты — файлы kimag.iib и kimag.lst, В файле подстановок kimag.gen содержится перечень имен OBJ-файлов, которые должны быть включены в создаваемую би- библиотеку. Ниже приведен пример такого файла.
Введение + IDVCHR ¦ IDVFLO ¦ ЖВУ11Т ¦ IDVYES ft + IHISAI ¦ EHISTA t + KIAFFI ¦ KIAPAR t ¦ IIBLOK ft + KDUGL t ¦ IINERR ft + IINGAU ¦ IIKGCR ¦ KINODI ¦ KIHGSQ ft •f KIKQTP + KIHGUB ¦ IIHGtfF ft + IllffllS 4- EUffiMA ¦ IIMSTA ft 4- KIRBXV + KTMSAV ¦ XI1SII ft + KIOPAI + KIOFEY + KIOFPG ft 4- KOSPTF Утилита tlib позволяет не только создавать библиотеку из набора OBJ-файлов, но также исключать и заменять отдельные модули. Советуем подробнее ознакомиться с ее возможностями ([15]). О тестовых программах Управление работой демонстрационных программ организовано в форме диалога, построенного по типу «вопрос-ответ». Это про- простейшая форма диалога, и она неоднократно подвергалась критике в основном по той причине, что машина играет в таком диалоге ведущую роль (задает вопросы — да как она смеет!), а человек — пассивную (т. е. дает ответы). Но мы выбрали все-таки эту фор- форму потому, что такой диалог, обеспечивая все наши потребности по управлению ходом обработки, легко программируется, без труда модифицируется и не загромождает рабочие программы. Кроме того, мм внесли усовершенствования, позволившие со- сократить число нажатий на клавиши, а в некоторых стандартных ситуациях вообще огр&ттчшться клавишей <EnterX Запрашивая ввод очередного параметра, наша программа одно- одновременно предлагает некоторое значение, выбранное по умолчанию. При согласии с ним достаточно нажать клавишу <Euter>. Если же вы вводите новое значение, то оно проверяется на допустимость подпрограммой ввода. Запрос повторяется до тех пор, пока не бу- будет введено правильное значение. Впрочем, там, где *то необходимо, мы используем простейший командный язык. Это относится к некоторым программам гл. 5. Желающих украсить свои программы изощренным человеко- машинным интерфейсом мы отсылаем к работе [17], в которой по- подробно рассмотрены многочисленные формы диалога и даны исчер- исчерпывающие рекомендации по их применению. Приводятся тексты программ на языке Паскаль; их перевод на Си не вызывает серьез- серьезных затруднений, так как большинство синтаксических конструк- конструкций Паскаля имеют аналоги в Си, Работа демонстрационной программы может быть в любой мо- момент прервана нажатием комбинации клавиш <Ctrl><C>.
ПРЕДСТАВЛЕНИЕ И ХРАНЕНИЕ ИЗОБРАЖЕНИЙ В этой главе помещены описания и тексты программ, дающих не- некоторый минимальный базовый набор инструментов для дальней- дальнейшей работы. Это программы формирования ряда тестовых изо- изображений, простейшие программы вывода изображений на экран (или в указанный файл) в виде числовых или символьных матриц, программы для сохранения изображений на магнитном диске и для сокращенного блочного кодирования изображений. 1.1. Представление изображений в программах Цифровые изображения (поля) принято представлять в виде ма- матриц (двумерных прямоугольных массивов чисел) с неотрицатель- неотрицательными элементами. Каждый элемент матрицы отвечает одному элементу изображения — пикселю. Упомянутый термин появился сравнительно недавно, это калька с английского сокращения Pixel, буквально переводимого как «элемент изображения» или «элемент картинки». Элементы матриц обозначаются как A{j, где первый индекс обо- обозначает номер строки, второй — номер элемента в строке (номер столбца). Число строк матрицы мы будем обозначать (в том числе и в текстах программ) через iV,-, число столбцов — через Nj. Ну- Нумерация начинается с единицы, строки нумеруются сверху вниз, столбцы — слева направо (рис. 1.1). Какой тип данных используется для хранения изображений? Обычно при программировании задач обработки изображений один пиксель отображается на один байт машинной памяти, что дает до 256 уровней яркости, от 0 до 255. Мы поступим так же, тем более что в Си имеется подходящий тип данных — unsigned char (байтовый, без знака). Такой выбор вовсе не обязывает нас использовать все 256 уровней, нередко мы будем ограничиваться
1.1. Представление изображений в программах 11 - 1 ... I i i ш 1...Щ кц кi2 • • • А21 А22 • • • A2j • • • A2Hj •••••*• Ai2 • • • Aij • • • AiNj AHil AHi2 • • • *Mij • • • AHiMj Рис. 1.1. Нумерация элементов матрицы. числом уровней 64 или 32. Этого вполне достаточно для удовлетво- удовлетворительного воспроизведения на экране тестовых изображений, так как именно такой порядок имеет количество различаемых глазом уровней яркости. Язык Си в отличие от многих других распространенных языков высокого уровня, таких, как Алгол, Фортран, Бейсик и ПЛ/1, не поддерживает передачу в подпрограммы двумерных массивов с пе- переменными верхними границами. Поэтому в программах на языке Си для хранения изображений мы будем использовать одномерные массивы. Изображение при этом разворачивается построчно, так что элемент Aij матрицы помещается в ячейку одномерного мас- массива с номером A.1) Элементы изображения занимают ячейки массива с номерами от 0 до N{ х Nj — 1. На рис. 1.2 показана нумерация элементов ма- матрицы при ее размещении в виде одномерного массива на примере матрицы размером 5x7 элементов. Одномерное представление матриц вызывает определенные не- неудобства при записи алгоритмов, снижается и наглядность про- программ, которые и без того малочитабельны из-за лаконичности языка Си. С другой стороны, при надлежащем программирова- программировании принятый нами подход может повысить эффективность про- программ. С чем это связано? Фактически, какой бы язык программирования ни использо- использовался, многомерные массивы отображаются в конечном счете на линейную (одномерную!) последовательность ячеек памяти — а именно так организована память существующих ЭВМ. Если за-
12 Глава 1. Представление и хранение изображений 1 2 3 4 5 1 0 7 14 21 28 2 1 8 15 22 29 3 2 9 16 23 30 4 3 10 17 24 31 5 4 11 18 25 32 6 5 12 19 26 33 '¦ ¦¦» 7 6 13 20 27 34 Рис. 1.2. Нумерация элементов матрицы при размещении в одно- одномерном массиве. дача определения адреса возлагается на транслятор, то вычисле- вычисления по формулам, аналогичным A.1), проводятся, как правило, для каждого (подчеркиваем — для каждого!) элемента масси- массива. Язык Си, перекладывая эту обязанность на программиста, создает предпосылки для более эффективной организации вычи- вычислений. Так, например, для последовательного перебора всех элемен- элементов изображения вообще нет необходимости использовать форму- формулу A.1) — достаточно сначала установить счетчик элементов в нуль, а затем добавлять туда единички. Иначе говоря, организо- организовать цикл от 0 до N{ х Nj — 1 с шагом 1. Если порядок обработки пикселей безразличен, то лучше поступить наоборот: организо- организовать цикл от Ni х Nj — 1 до 0 с шагом — 1. Такой вариант даст более эффективный машинный код, так как условие окончания цикла может быть записано как сравнение значения счетчика с нулем. При обработке прямоугольного фрагмента поля йыражение A.1) следует использовать только для вычисления адреса первого элемента очередной строки фрагмента. Продвижение вдоль стро- строки осуществляется добавлением единичек в счетчик. В программах применяются пользовательские, или специализи- специализированные, типы данных. Например, изображения записываются в массивы элементов типа SHOW. Этот тип объявлен как синоним типа unsigned char. Для пересчета элементов изображения исполь- используются переменные типа COUNT, объявленного синонимом типа unsigned int. Определения типов сосредоточены в головном файле IMAGE.H
1.1. Представление изображений в программах 13 (листинг 1.1). Такое описание специализированных типов дан- данных, характерное для языка Си, открывает богатые возможно- возможности, недоступные для большинства других языков программиро- программирования. Что это за возможности? Упомянем две из них. Во-первых — мнемоника. Специализированные типы данных, если ими пользо- пользоваться систематически, облегчают понимание программы, позво- позволяя быстрее понять смысл и назначение используемых объектов. Так, увидев массив, описанный как SHOW, мы сразу знаем, что он предназначен для хранения изображений и будет использовать- использоваться в программе именно для этих целей. Если не применять тип SHOW, то массивы unsigned char, хранящие изображения, было бы сложнее отличить от массивов того же типа, используемых в других целях. Во-вторых, специализированные типы данных облегчают вне- внесение изменений в программы. Поясним это примером. Тип SHOW определен нами как синоним типа unsigned char, что со- соответствует диапазону значений от 0 до 255. Если же опре- определить его как char, можно будет обрабатывать изображения с диапазоном уровней от —128 до 127. Поскольку тип определя- определяется один раз, то не потребуется кропотливая коррекция опи- описателей во множестве модулей — достаточно внести единствен- единственное исправление в головной файл IMAGE.H и перетранслировать все подпрограммы. Здесь, однако, следует проявлять осторож- осторожность, так как некоторые программы все же потребуется перера- переработать. Переменные типа COUNT в наших программах могут прини- принимать значения от 0 до 65535, что позволяет адресовать элементы изображения размером до 256 х 256 пикселей. Файл IMAGE.H содержит и некоторые другие полезные опре- определения. В частности, там находится макроопределение, реализу- реализующее выражение A.1). На листинге 1.1 вместе с головным файлом IMAGE.H, ис- используемым многими программами описываемого пакета, пока- показан модуль KIMAGL.C. Этот маленький, но очень важный мо- модуль, хранящийся в отдельном файле, — даже не подпрограмма. KIMAGL.C содержит лишь описание и задание начальных зна- значений часто используемых текстовых строк. Поскольку упомя- упомянутые строки в явном виде фигурируют только в головном фай- файле IMAGE.H, мы поместили эти два модуля на одном листинге, нарушив тем самым собственное правило: один листинг — один файл.
14 Глава 1. Представление и хранение изображений Листинг 1.1. Файл глобальных переменных и головной файл IMAGE.H /¦ Файл ~ KINAOL.C */ /* Глобальные переменные */ char *SS. « " Обработка прекращена нэ-ва ошибки.", ¦S6. ш •» обработка завершена.11, ¦S7. * " Для выхода в снстеку пахните лабуа клавишу ", «S8. * " Для продолжения нажмите лвбуа клавишу "; /¦ ФАЙЛ — IMAGE.И */ •include <stdlib.h> •include <conio.h> extern char *SS., *S6., *S7., *S8.; •define HARK.(i,j) <i-l)*Ij*(j-l> /¦ Для расчета адреса ¦/ •define PAUSE. { printf ("\n%*", SO; g«tchO; put*CMI); } •define STOP. { printf ("X»\nX»", S5.tS7.); getehO; exit(O); > •define EID. { printf <nXe\nl»M, S6.,S7.); getchO; exit(O); } •define loop for < ; ; ) /* Бесконечный цикл •define MURMAX 25S /¦ Н&ксинально-допуст. уровень */ •define HODATA 66 /¦ Для необработанных пикселей */ typedef unsigned char SHOW; /¦ Для описания изображений */ typedef unsigned char UPAK; /¦ Для блочного кодирования ¦/ typedef unsigned int COUNT; /* Для счетчиков элементов поля */ •include "KIMAG.H" /* Вклвч&ем файл прототипов +/
1.2. Формирование тестовых изображений 15 ( 1 ! 1 ! 2 ! 3 ! 4 ! 5 ! 6 ! 7 ' 8 ! 9 ! 10 ! 11 ! 12 i ) 0 1 ! 2 ' 3 • 4 ! 5 ! 6 ! 7 > 8 ! 9 ! 10 ! 11 1 2 3 4 5 6 7 8 9 10 11 12 2 3 4 5 6 7 8 9 10 11 12 13 3 4 5 6 7 8 9 10 11 12 13 14 5 • 4 5 6 7 8 9 10 11 12 13 14 15 5 6 7 8 9 10 11 12 13 14 15 16 6 7 8 9 10 11 12 13 14 15 16 0 7 8 9 10 11 12 13 14 15 16 0 1 8 9 10 11 12 13 14 15 16 0 1 2 10 * 9 10 11 12 13 14 16 16 0 1 2 3 10 11 12 13 14 15 16 0 1 2 3 4 11 12 13 14 15 16 0 1 2 3 4 5 12 13 14 15 16 0 1 2 3 4 5 6 13 14 15 16 0 1 2 3 4 5 6 7 Рис. 1.3. Поле, сформированное функцией KIMGDI. Максималь- Максимальный уровень = 16. 1.2. Формирование тестовых изображений Для формирования детерминированных тестовых полей заданно- заданного размера служат подпрограммы KIMGDI, KIMGCR, KIMGTP и KIMGSM. Подпрограмма KIMGDI, приведенная на листинге 1.2, осуще- осуществляет диагонально-ступенчатое заполнение матрицы в соответ- соответствии с выражением Aij=modk(i + j-2), A.2) где к — заданное число уровней (от 2 до 256). Элементы матрицы при этом получают значения из диапазона от 0 до к—1. Например, матрица размером 12x14 элементов при 17 уровнях имеет вид, показанный на рис. 1.3. Отметим, что в этой, как и в других подпрограммах, входным параметром является не количество уровней Jb, а максимальный уровень Миг. Очевидно, что Mur = Jb — 1. Листинг 1.2. Функция KIMGDI •include <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIMGDI.С ¦/ void RIMGDI <I»*g«,Mi,Mj,Mur> SHOW Inag«Q;
16 Глава 1. Представление и хранение изображений int Ii,Ij,Mur; /# „ „ . ¦/ /¦ ГЕНЕРАЦИЯ ТЕСТОВЫХ ИЗОБРАЖЕНИЙ */ /¦ Дя«гоя&лыю« заполнение матрицы ¦/ /¦ Миг * максимальный уровень ( уровни 0...Миг ) */ { int i,j; i»t Kur«f!ur*l; СОШГТ k«0; printf("KIHGDI: Диагональное з&яоятокяв ноля %А * %d\n"9 KIKEERC'KIHODIt'SMiyBj); /• * Контроль размеров поля if («ur<2 tt for ( i»l; i<»Ii; i-м-) for ( j*l; j<»Ij; j**) Ieage [к**] * (i<j-2) X Kur; Подпрограмма KIMGCR (листинг 1.3) заполняет матрицу в со- соответствии с уравнением конуса, вершина которого находится в центре поля, а основание вписано в прямоугольную рамку его гра- границ. Кроме того, угловым элементам поля присваиваются значе- значения максимального уровня. Матрица размером 11 х 13 элементов, заполненная подпрограммой KIMGCR при 17, уровнях показана на рис. 1.4. Еще один пример дан на рис. 1.8. Примеры обращений к подпрограммам KIMDGI и KIMGCR можно найти в подпрограмме KIMGAU (листинг 1.8). О 5 10 »яттт—тт——тттттт «»•"— в """""""""""""^.""""""" "** """™"*""" 1! 16 00223332200 16 2! 0124566654210 3! 1346789876431 4 ! 2 4 6 8 9 11 11 11 9 8 6 4 2 5 ! 3 5 7 9 11 13 14 13 11 Э 7 5 3 6 ! 3 5 7 10 12 14 16 14 12 10 7 5 3 7 ! 3 5 7 9 11 13 14 13 11 9 7 5 3 8! 24689 11 11 11 98642 9! 1346789876431 10! 0124566654210 11 ! 16 00223332200 16 Рис. 1.4. Поле, созданное функцией KIMGCR.
1.2. Формирование тестовых изображений 17 Листинг 1.3. Функция KIMGCR •include <stdio.h> finclude <math.h> •include "IMAGE.H" /¦ ФАЙЛ — KIMGCR.С ¦/ void KIMGCR (Image,Mi,Mj,Mur) SHOW Imaged; int Ni,Mj,Mur; /¦ ГЕНЕРАЦИЯ ТЕСТОВЫХ ИЗОБРАЖЕНИЙ ¦/ /* Концентрическое заполнение поля */ /¦ По уравнении конуса z « k*SQRT(x**2*y**2) ¦/ /* Mur - максимальный уровень ( уровни О...Миг ) */ int i,j,Im,Jm,t; float A,B,Ci,Cj; double C,D; printf("KIMGCR: Концентрическое заполнение поля %d * %d\n",Mi,Mj); KIMERR("KIMGCR:",Hi,Mj); /¦ - Контроль размеров поля ¦/ if (Mur<l || Mur>MURMAX) Mur » MURMAX; Ci « 0.5*(Mi-l); 1ю«(МгЮ/2; A « Mur/( (float) Im); Cj * 0.5*(Mj-l); Jm«(Mj*l)/2; В * Mur/( (float) Jm); for (i=0; i<Im; i*-O < С * A*(ci-i)*A*(Ci-i); for (j=0; j<Jm; j**) { D » B*(Cj-j)*B*(Cj-j); L * Mur - (int) sqrt( C*D ); if ( L>Mur ) L«Mur; if ( L<0 ) L*0; Image [MARK.(l*i,l*j)] * L; Image [MARK.(Mi-i,l*j)] « L; Image [MARK.(l*i,Mj-j)] » L; Image [MARK.(Mi-i,Mj-j)] * L; } Image [MARK_A,O] «Mur; Image [MARK.(Mi,D] «Mur; /¦ Углы ¦/ Image [MARK.d,Nj)] =Mur; Image [MARK.(Ii,Mj)]«Mur; /¦ поля ¦/ Подпрограмма K1MGTP (листинг 1.4) создает поле, содержа- содержащее фигуру с резко очерченными границами. Фигура эта похо- похожа на асимметричный оконный переплет, заключенный в рамку. Это поле будет использовано при рассмотрении геометрических преобразований изображений. Уровни, которыми заполняются
18 Глава 1. Представление и хранение изображений 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 5 10 15 20 "*""""""""""""""" • ¦"¦"¦"¦*¦"¦"•*¦"¦", ¦»"•¦»¦»¦»¦»¦»¦»¦" # ¦»¦»¦»¦•¦»¦»¦»¦»¦» ^ .111111111111111111. 11 11 1 1 1.. 8888888888888... 1 1. • о о • . • • о о о • • о о • • • 1 1* • о о • • • • о о о • • о о • • • 1 1* • о о • • • • о о о • . о о • • . 1 1* • о о • • • • о о о • • о о • • • 1 1* . о о • • • • о о о • . о о • • • 1 1* • о о • • • • ооооооо. • • 1 1.. 8888888888888... 1 1. • о о • • • • о о • • • о о • . .1 х. • О О • • . • О О • . • О О • . . X 1. • о о • • • • о о • • • о о • • • 1 1* • о о • • • • о о • • • о о • • • 1 1.. 8888888888888... 1 1 1 1 1 11 11 .111111111111111111. Рис. 1.5. Поле, созданное функцией KIMGTP. элементы, принадлежащие фигуре и рамке, задаются независимо друг от друга, фон имеет нулевой уровень. На рис. 1.5 показано такое поле размером 20 х 20 элементов, отображенное подпрограм- подпрограммой КЮТС. KIMGTP использует две вспомогательные подпрограммы, на- находящиеся в том же файле. Это KIGPNT, выводящая точку, и KIGLIN, формирующая в матрице линию. Уровень, которым за- заполняются точки и линии, определяется параметром L. KIGLIN использует алгоритм Брезенхема, известный из литературы [3, т. 2, стр. 140]. Обе подпрограммы имеют защиту от выхода за границу поля, в котором формируются точки и линии. Защита состоит в том, что если координата точки задана вне допустимых границ, то исполь- используется остаток от деления заданной координаты на размер поля. Оператор, осуществляющий такой пересчет, оформлен в виде ма- макроопределения. Подпрограммы KIGPNT и KIGLIN представля- представляют и самостоятельный интерес, так как могут быть полезны при синтезе графических изображений.
1.2. Формирование тестовых изображений 19 Листинг 1.4. Функции KIMGTP, KIGPNT, KIGLIN finclude <stdio.h> finclude "IMAGE.HM /¦ ФАЙЛ ~ KIMGTP.С ¦/ void KIMGTP (Image,Hi,Nj,Ul,U2) SHOW I «age [] ; int Ni,Nj,Ul,U2; /¦ ¦/ /¦ ГЕНЕРАЦИЯ ТЕСТОВЫХ ИЗОБРАЖЕНИЙ */ /¦ Тестовое поле типа "рама" */ /¦ Ul, U2 - уровень рамки и уровень картинки */ C0UHT к; int i,j; int ll-Nj/20, 12«Nj/10; /¦ Ширина вертикалей ¦/ int 13«Ni/22, 14«Ni/12; /¦ Ширина горизонталей */ int Jl«Nj/5, J««Nj/2, Jr«3*Mj/4; /¦ Вертикали ¦/ int Iu*Ni/5, I««Hi/2+l, Id«3*Hi/4*l; /¦ Горизонтали ¦/ printf("KIMGTP: Создание поля типа 'Рана' %d * %d\n\n">Ni,Nj); /* - Контроль размеров поля */ for (k«(long)Hi*Hj; k>0; ) Iiage[—k]*0; /¦ Очистка поля ¦/ KIGLIN (lMage,Ni,Nj, KIGLIN (iMage.Ni^Nj, KIGLIN (lHage,Ni,Nj, KIGLIN ClHagetNi,Nj, KIGPNT (lHage,Ni,Nj, KIGPNT KIGPNT KIGPNT (lMage,Ni,Nj, for (i»Iu; i<«Iu+13; for (i«Id; i<«Id+13; for (i=I«; i>»Im-13; for (i«Im; i>«Ii-14; for (j«Jl; j<«Jl+ll; for (jsJr; j<sJr+ll; for (j*Jm; j<«Ji+ll; for (j*Jm; j<=J«+12; 1, 2 Ni,2 2, 1 2,Nj 2,2, 2,Nj Ni-1 Ni-1 1, Nj-1, Ul); Ni,Nj-l, Ul); Ni-1, 1, Ul); Ul); /¦ Рамка поля ¦/ /¦ Углы ранки i++) i—) i—) Ul); -1, Ul); ,2, Ul); ,Nj-l, Ul); /¦ Картинка - горизонтали KIGLIN (Image,Ni,Nj,i,Jl,i,Jr,U2); KIGLIN (Image,Ni,Nj,i,Jl,i,Jr,U2); KIGLIN (Image,Ni,Nj,i,Jl,i,Jr,U2); KIGLIN (Image,Ni,Nj,i,Jm,i,Jr,U2); /* Вертикали */ KIGLIN (Image,Ni,Nj,Iu,j,Id,j,U2); KIGLIN (Image,Ni,Nj,Iu,j,Id,j,U2); KIGLIN (Image,Ni,Nj,Iu,j,id,j,U2); KIGLIN (Image,Ni,Hj,Iu,j,Im,j,U2); /* Графические примитивы •define FT0R(x,Nx) if ( (x « (x-l)XNx + 1) <« 0 ) x+«Nx
20 Глава 1. Представление и хранение изображений void KIGPMT (Image, Hi.Mj, i,j, L) SHOW Image []; int Mi,Mj; int i,j, U /«, */ /¦ Точка (i,j) на дискретной поле ¦/ /* ВНИМАНИЕ: поле свернуто в тороид */ FT0R(i,Ni); FTOR(j,Mj); /¦ Коррекция выхода за границы поля */ Image [MARK_(i,j>] * L; void KIGLIM (Image, Ni,Hj, il,jl, i2,j2, L) SHOW Imaged; int Hi,Mj; int il,jl, i2,j2, L; /¦ Проведение пряной на дискретной поле. */ /* Алгоритм Брезенхена. */ /* ВНИМАНИЕ: поле свернуто в тороид */ /* il,jl - начальная точка */ /¦ i2,j2 - конечная точка ¦/ /* L - уровень, который заполняется пряная */ /«, „/ { int delt.i, delt.j; int Dx,Dy, eps, ex,ey, stx,sty; int t; FT0R(il,Mi); FTOR(jl,Ij); /¦ Коррекция выхода за границы поля */ FTOR(i2,Mi); FT0R(j2,Nj); delt.i * i2-il; delt.j « J2-J1; Dx * abs(delt_i); Dy * abs(delt.j); stx = ( delt_i>0 ) ? 1 : -1; /¦ шаги по координатам */ sty * ( delt_j>0 ) ? 1 : -1; if (Dx >« Dy) { eps * 2*Dy - Dx; ey = 2*Dy - 2*Dx; ex s 2*Dy; for (t*0; t<«Dx; t++) { Image [MARK.(il,jl)] ¦ L; /¦ printf ("— x-%4d, y»y.4d\n",il,jl); ¦/ il +« etx; if (eps>0) { jl +* sty; eps +* ey; > else eps += ex; } else { eps ¦ 2*Dx - Dy; ey * 2*Dx - 2*Dy; ex * 2*Dx;
1.2. Формирование тестовых изображений 21 for (t*0; t<*Dy; t++) { Image [MARK.Gl,jl)] * L; /¦ printf ("|| x»X4d, y*X4d\n",il,jD; ¦/ jl +» sty; if (eps>0) { il +* stx; eps +s ey; } else eps ¦¦ ex; } Подпрограмма KIMGSQ (листинг 1.5) создает квадрат на пу- пустом поле. Квадрат заполняется пикселями заданного уровня; положение квадрата задается в диалоговом режиме. Эта подпро- подпрограмма будет использоваться нами в гл. 5 при иллюстрации работы алгоритмов фильтрации полей. Листинг 1.5. Функция KIMGSQ •include <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIMGSQ.С ¦/ •include "KDV.H" void KIMGSQ (Image,Ni.Mj.Mur) SHOW Imaged; int Mi,Mj,Mur; /«, */ /* Черный квадрат (Почти по Малевичу) */ /* Уровни: фон * 0, квадрат * Миг */ int Gmax * (Mi<Mj) ? Ni : Mj ; static int Gl=3, G2=8; COUMT k » (long)Ni*Mj; printf ("KIMGSQ: Черный квадрат на поле %d * %d\n", Mi,Mj); while (k) Image [—k] * 0; KDVINT ("Левый верхний угол квадрата Gl", ftGl, l,Gmax); KDVIMT ("Правый нижний угол квадрата G2", ftG2, Gl,G»a.x>; for (i=Gl; i<=G2; i for (j*Gl; j<*G2; j++) Image[MARK.(i,j>3 * Mur; Подпрограммы KIMGUR и KIMGWF позволяют получать псев- псевдослучайные многоуровневые поля.
22 Глава 1. Представление и хранение изображений 0 1 I 2 ' 3 4 5 6 < 7 i 8 9 10 11 12 13 14 15 16 ! 7 ! 2 ! 2 ! 1 ! 2 ! 3 ! 1 ! 3 ! 2 ! 8 ! a ! 8 ! 4 ! . ! 9 ! 8 1 2 6 . 6 2 7 8 5 9 7 1 3 2 8 a 1 4 a 5 8 7 4 7 a 9 4 7 a 3 7 8 3 6 4 9 6 7 1 6 6 8 8 5 . 4 8 5 . 8 8 3 5 1 a 8 a 4 a 1 6 6 5 1 6 # 4 1 1 5 . 6 2 2 9 . 2 8 a 1 a 8 a 3 5 6 2 a 1 a 2 4 8 7 4 1 6 1 6 1 3 9 1 1 5 2 a 3 3 9 3 1 a 1 . 7 5 9 4 7 . 7 8 4 9 4 10 8 9 # 6 9 5 9 6 4 8 6 7 9 a a 7 . 2 4 8 4 9 5 7 5 2 4 4 8 8 1 1 2 5 3 6 8 2 8 7 5 8 6 5 1 . 3 9 1 6 4 1 6 3 2 4 4 2 1 3 4 9 9 2 1 3 3 7 1 9 5 8 5 7 15 9 2 8 1 8 1 6 4 6 a 2 3 2 a a 1 9 4 . 6 6 9 1 6 1 2 8 3 6 2 3 a a a 8 5 7 3 5 3 5 5 9 2 1 1 3 5 a 6 9 6 2 4 6 7 5 1 1 3 6 a 6 3 20 3 8 3 6 2 3 3 4 4 3 2 a 5 1 1 6 2 # 8 8 6 3 8 4 1 8 4 4 9 5 Рис. 1.6. Пример псевдослучайного поля со статистически незави- независимыми отсчетами, созданного функцией KIMGUR. KIMGUR (листинг 1.6) формирует поле со статистически неза- независимыми в совокупности отсчетами. Пример такого поля приве- приведен на рис. 1.6. Подобные поля часто называют некоррелирован- некоррелированными, что короче, но не совсем верно. Отсчеты сформированного поля имеют уровни от 0 до Миг. Для получения такого поля используется датчик случайных чисел random(m) из библиотеки Турбо-Си. Эта функция выдает целые случайные числа, равномерно распределенные в диапазоне (О, m — 1). Существует множество подобных функций. Если вы работаете не в Турбо-Си, то название и параметры датчика следу- следует уточнить, обратившись к документации. Изучив описание ис- используемой библиотеки стандартных функций, вы научитесь ме- менять вид получающейся картинки, варьируя начальное значение датчика. Листинг 1.6. Функция KIMGUR •include <8tdio.h> •include "IMAGE.H" /* ФАЙЛ — KIMGUR.С ¦/ void KIMGUR (Image,Ni,Ij,Mur) SHOW Imaged;
1.2. Формирование тестовых изображений 23 int Ni,Nj,Mur; /* Поле случайных чисел с равномерным распределение»* */ /* Уровни: 0 ... Миг */ /* ¦/ { COUNT к * (long)Ni*Hj; KIMERR (" KIMGUR:", Ni,Nj); while (к) Image[--k] ¦ random (Миг+1); Подпрограмма KIMGWF (листинг 1.7) дает случайное поле с коррелированными отсчетами. Для его создания сначала форми- формируется первичное некоррелированное поле, которое затем подвер- подвергается низкочастотной фильтрации. Алгоритм фильтрации несложен и состоит в следующем. По первичному полю двигается прямоугольная апертура (окно) за- заданного размера. Все отсчеты, попадающие в это окно, сумми- суммируются. После нормировки, о которой будет сказано ниже, полу- полученное значение записывается в выходное поле, в тот его отсчет, который соответствует левому верхнему углу скользящего окна. Обычно с текущим отсчетом ассоциируется центр окна, но это имеет смысл в процедурах фильтрации, где нежелательно вносить сдвиг, здесь же — безразлично. Обратим внимание на то, что первичное поле должно иметь размеры, превышающие размеры требуемого коррелированного поля (при рассмотрении алгоритмов фильтрации изображений бу- будет показана возможность снять это требование). Память для хра- хранения первичного поля распределяется динамически с помощью функции malloc и освобождается при выходе из подпрограммы. Вернемся теперь к вопросу нормировки поля, полученного в результате сканирования окна. Один из вариантов: сначала за- записать все полученные суммы sum,^ в клетки выходной матрицы, определить среди них минимальное 5min и максимальное Smax зна- значения, а затем провести нормировку по формуле: A.3) min где Aij — новое значение текущего отсчета, Миг — требуемый максимальный уровень.
24 Глава 1. Представление и хранение изображений Однако такой способ имеет существенный недостаток. Дело в том, что полученные значения сумм в общем случае нельзя запи- записать в массив, предназначенный для хранения результирующего поля! Ведь это массив байтов, допускающий значения от 0 до 255, в то время как сумма в пределах окна может быть существенно больше. Например, при 64 уровнях первичного поля и окне 4x4 элемента сумма принимает значения от 0 до 16 х 63 = 1008. Поэто- Поэтому для хранения промежуточных результатов потребуется массив чисел типа int или unsigned. Мы пойдем другим путем. Попробуем оценить заранее значе- значения 5min и iSmax- Вспомним, что первичное поле заполнено чи- числами, равномерно распределенными от 0 до Миг. Из теории ве- вероятностей [18, 19] известно, что среднее значение такого поля равно Mur/2, а среднеквадратическое отклонение S = Mur/\/l2. Обозначив через G число элементов поля, накрываемых окном, и не забывая, что суммируются независимые случайные числа, по правилам теории вероятностей получаем среднее значение М и среднеквадратическое отклонение 5 образованных сумм Gx Mur fU t ч 5 Му —. A.4) Те, кто помнит Центральную предельную теорему теории веро- вероятностей, уже поняли, что закон распределения полученных сумм близок к нормальному (мы не будем напоминать, что популярный метод получения нормально распределенных случайных чисел как раз и состоит в суммировании 12 чисел с равномерным распреде- распределением). Большинство значений нормальной случайной величины концентрируется вблизи среднего. По известному правилу «трех сигм» [19, стр. 191] более 99,7% всех значений отстоит не далее чем на 35 от среднего. В пределах двух сигм находится 95,4% значений. Обозначив число «сигм» через д, запишем Smin = M-QXS, Smax = М + q X 5. Тогда правило нормировки примет вид / о \ МиГ ij = (sum,7 - 5min) x 2xgxg- Именно так и сделано в подпрограмме KIMGWF. Количество «сигм» регулируется параметром QSG, который находится вну- внутри подпрограммы (соответственно при его изменении необходимо перетранслировать подпрограмму). Кроме того, введены условные операторы, не позволяющие полученным в результате преобразова-
1.2. Формирование тестовых изображений 25 с 1 ! 2 ! 3 ! 4 ! 5 < 6 7 8 9 10 11 12 13 ' 14 < 15 16 < ) i—... 1 6 > 6 ! 5 ! 6 ! 4 i 2 ! 1 ! . * • ! . I . ! 1 ! 4 ! 5 ! 6 6 7 4 6 4 3 2 3 1 3 4 6 4 4 2 3 3 1 # 1 . 1 3 4 4 4 4 3 4 3 1 # 1 1 2 5 4 4 5 1 1 . 2 2 2 1 1 2 2 4 5 7 6 6 3 2 3 4 3 3 2 2 1 5 6 6 5 9 7 6 3 3 3 4 3 1 2 2 1 5 8 8 а а а 8 3 4 4 6 5 3 4 4 3 6 9 8 9 а а 9 4 5 4 5 5 3 4 5 5 7 а 9 9 9 9 7 10 — 1. 8 8 8 7 7 2 4 3 4 5 а 8 8 8 8 7 9 9 9 7 7 3 4 4 5 6 а 7 7 6 6 5 8 8 9 7 9 5 6 5 6 4 7 5 5 3 4 3 9 9 а 7 7 5 6 5 6 5 8 5 5 3 4 3 8 9 а 8 9 7 7 6 7 5 7 5 5 4 4 3 15 7 9 а 9 а 9 8 7 7 5 5 3 4 3 2 2 7 9 9 9 а 9 8 7 7 5 6 4 6 5 4 2 а а 9 а 9 7 7 7 5 6 7 5 6 6 4 2 8 а 9 а 8 7 6 6 5 7 7 6 8 7 5 3 6 8 6 8 6 5 4 4 3 5 6 6 8 7 7 6 20 4 4 3 5 2 1 2 4 3 5 7 7 9 7 8 6 Рис. 1.7. Псевдослучайное поле с коррелированными отсчетами, сформированное функцией KIMGWF. Максимальный уровень = 10, размер скользящего окна — 5x5 элементов. ния значениям выходить за пределы допустимого интервала уров- уровней 0. ..Миг. Полученное поле (рис. 1.7) имеет гистограмму уровней, близ- близкую к нормальному закону распределения. Иногда желательно иметь коррелированное поле с равномерным распределением. Для этого необходимо применить к нему процедуру выравнивания ги- гистограммы. Мы еще вернемся к этому вопросу в одной из после- последующих глав. Листинг 1.7. Функция KIMGWF «include <stdio.h> finclude <math.h> •include "IMAGE.H" /¦ ФАЙЛ — KIMGWF.С ¦/ tdefine QSG 2.5 Ўoid KIMGWF (Ieage,Ii,Ij,Mur, Fi,Fj) SHOV lB&ge[]; int Ii,Ij,Mur, Fi,Fj; /¦ */ /* Поле коррелированных случайных чисел, полученных */
26 Глава 1. Представление и хранение изображений /* из некоррелированного поля сканированием окна */ /¦ Уровни: 0 ... Миг ¦/ /¦ Fi,Fj - размеры окна ¦/ SHOW *Ich; /¦ Указатель на вспомогательное поле */ int register вив; int i,j, p,q; int Ri«Ni+Fi-l, Rj*Nj+Fj-l, frag»Fi*Fj; COUNT k,kl,k2,k3, т,ш1; COUNT Kim * (long)Ri*Rj; COUNT Vol; float sigma * Mur*sqrt (frag/12.OL); float Smin * 0.5*Mur*frag - QSG*sigma; float aft ¦ Mur/B*QSG*sig«a); if (sigma < 0.0) sigma*0.0; KIMERR (" KINGWF: задан",Ni,Nj); /* - Контроль размеров */ Vol * KiB*sizeof(SHOW); Ich * (SHOW *) aalloc (Vol); if Olch) { printfC'KIMGWF: ну нет %\i байт!\n",Vol); STOP. } k » Kia; while (k) Ich[—-k] » randoB(MurH); for (kl*0, Bl»0, for (k2«kl,B«Bl, j«l; j<«Nj; j++, k2++, { 8ub*0.0; for (k3«k2, p«0; p<Fi; p++, k3+«Rj) for (k«k3, q»0; q<Fj; q++, k++) suB+«Ich[k]; p ¦ (euB-SBin)*aft +0.5; if (p<0) p«0; else if (p>Mur) p«Mur; Ca] ¦ p; free (Ich); Подпрограмма KIMGAU (листинг 1.8) объединяет все шесть описанных выше подпрограмм формирования тестовых полей, по- позволяя выбирать любую из них в интерактивном режиме. Будьте внимательны при обращении к ней. KIMGAU возвращает коррек- корректируемое внутри нее значение максимального уровня поля, поэто- поэтому соответствующая фактическая переменная (последний, четвер- четвертый параметр подпрограммы) должна быть снабжена при обра- обращении знаком амперсенда. В отличие от вышеописанных подпро- подпрограмм типа void KIMGAU имеет тип int и возвращает значение — условный номер вызванной внутри нее функции.
1.2. Формирование тестовых изображений 27 Листинг 1.8. Функция KIMGAU ¦include <stdio.h> ¦include "IMAGE.H" •include "KDV.H" /* ФАЙЛ — KIMGAU.С */ int KIMGAU (Image, *i,*j, SHOW Imaged; int Hi,Hj,*lv; /¦ /* Формирование тестового поля с диалоговым выбором алгоритма /¦ { static int Nuraa=2; puts ("\n\t Алгоритмы формирования изображения:"); puts ("\t 1 - KIMGCR, 2 - KIMGDI, 3 - KIMGSQ'1); puts ("\t 4 - KIMGTP, 5 - KIMGUR, 6 - KIMGWF"); KDVINT ("Укажите номер алгоритма A...6)",ftNuma,l,6); KDVIHT ("Макс, уровень",1v,1,MURMAX); switch { case case case case case ( 1: 2: 3: 4: 5: Numa ) KIMGCR KIMGDI (Image, (Image, KIMGSQ (Image, KIMGTP KIMGUR (Image, (Image, Ni,Nj, Ni,Nj, Ni,Nj, Ni,Nj, Ni,Nj, *lv); *lv); *lv); *lv/3, *lv); ¦lv); break; break; break; break; break; case 6: KIMGWF (Image, Ni,Nj, *lv, Ni/4, Nj/4); return Numa; Создать и просмотреть случайное поле на экране можно с по- помощью демонстрационной программы KIMG4, показанной на ли- листинге 1.9. Использованные в ней функции диалогового ввода чи- чисел и символьного вывода поля на экран описаны в следующем разделе. Листинг 1.9. Тестовая программа KIMG4 tinclude <etdio.h> tinclude "IMAGE.H" •include "KDV.H" tdefine IMLEK 2048 /¦ ФАЙЛ — KIMG4.C */ /* Псевдослучайное заполнение */ /* поля и просмотр на экране */
28 Глава 1. Представление и хранение изображений void mainO { static SHOW Image[IMLEN]; int Hi=20, Hj«32; int Lm=32, Fi«2, Fj=2, ord*l; char yes * 'I*; puts ("\nKIMG4 - Псевдослучайное заполнение поля и просмотр"); do { KDVINT ("Высота поля, строк Hi",*Hi,1,220); KDVIHT ("Ширина поля, столбцов Hj",*Hj,O,IMLEH/Ni); KDVIHT ("Максимальный уровень поля, Lm", feLm,1,127); KDVIHT ("Поле: 1-некоррелированное, 2-коррелир.",feord,l,2); if (ord*«l) KIMGUR (Image,Hi,Hj,Lm); /¦ Некоррелир.поле ¦/ else { KDVIHT ("Размер окна сглаживания, Fi", &Fi,l,Hi); KDVIHT ("Размер окна сглаживания, Fj", &Fj,l,Hj); KINGWF (Image,Hi,Hj,Lm,Fi,Fj); /* Коррелированное поле */ } KIOTO (Image,Hi,Hj); /* Вывод поля на экран */ } while ( IKDVYES ("Закончить?", ftyes) ); EHD_ На листинге 1.10 показаны две подпрограммы контроля: раз- размеров изображения KIMERR и размеров фрагмента KIMERS. Листинг 1.10. Функции KIMERR и KIMERS ¦include <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIMERR.С ¦/ void KIMERR (text,Hi,Hj) char *text; /* Заголовок сообщения */ int Hi,Hj; /* Контролируемые размеры */ /¦ ¦/ /* Контроль размеров поля */ /* При ошибке - сообщение и выход в систему */ /«¦ ¦/ { int Егя0; if (Hi<l || Hi>1024) { printf(Cs Hi*5Cd ??\n", text,Hi); Er=l; } if (Hj<l || Hj>1024) { printf("y.s Hj=y.d ??\n", text,Hj); Er=2; >
1.3. Символьный и числовой вывод 29 if ( (long)Ni ¦ (long)Nj >« 65536 ) /¦ 65536 * 256 ¦ 256 ¦/ { pnntf ("%s большой размер поля - %d * Xd ??\n", text, Ni,Nj); Er=3; } if ( Er ) STOP. void KIMERS (text, Ni,Nj, Nil,Ni2, Njl,Nj2) char «text; /* Заголовок сообщения */ int Ni,Nj; int *Nil,*Ni2, *Njl,*Nj2; /¦ */ /¦ Контроль размеров поля и фрагмента поля */ /* При ошибке - сообщение и выход в систему */ /* Выход фрагмента за границу поля ИСПРАВЛЯЕТСЯ */ /* Ni, Nj - размеры матрицы */ /* Nil, Ni2 - первая и последняя строка фрагмента */ /* Njl, Nj2 - первый и последний столбец фрагмента */ { char *me = " Неверны границы фрагмента11; KIMERR (text,Ni,Nj); if ( *Nil<l ) *Nil * 1; if ( *Ni2>Ni ) *Ni2 = Ni; if ( *Njl<l ) *Njl ¦ 1; if ( *Nj2>Nj ) *Nj2 ¦ Nj; if (*Nil>*Ni2) { pnntfC1 y.s#/.s по строкам.\n'\ text,me); STOP. } if (*Njl>*Nj2) { pnntfC11 %s%b по столбцам.\n",text,me); STOP. } } 1.3. Символьный и числовой вывод В данном разделе описываются подпрограммы форматного выво- вывода полей на экран, принтер или в файл. Изображения при этом принимают вид прямоугольных таблиц, заполненных числами или символами. Примеры такого вывода приведены в предыдущем раз- разделе. Недостатком форматного вывода является его малая нагляд- наглядность, явным достоинством — переносимость в другие системы программирования на Си, так как используются только стандарт- стандартные конструкции языка. Форматный вывод на экран незаменим при отладке программ; вывод в файл или на печать полезен при подготовке отчетов. Ма- Матрицы, выведенные в файл, можно затем обрабатывать обычными текстовыми редакторами и объединять с другими текстами. При числовом выводе каждый элемент поля отображается в де- десятичное число, занимая заданное количество знакомест (не менее трех).
30 Глава 1. Представление и хранение изображений При символьном выводе каждый элемент поля отображается в один символ, символы могут разделяться пробелами. Исполь- Использование символьного вывода оправдано в тех случаях, когда изо- изображение имеет не 256, а существенно меньшее число уровней. Сопоставив каждому уровню какой-либо символ, мы получим воз- возможность уменьшить число знакомест, требуемое для отображе- отображения одного элемента поля, до двух (или даже до одного, если не разделять символы пробелами). При этом в строке экрана умеща- умещается 40 (или даже 80) элементов поля. Десяти цифр, 26 строчных и 26 прописных букв латинского алфавита и нескольких других символов достаточно для передачи более чем 64 уровней. Мы огра- ограничимся алфавитом из 68 символов, причем для большей нагляд- наглядности будем изображать нулевой уровень поля точкой вместо нуля. Соответствие между уровнями поля и выводимыми символами да- дано в таблице 1.1. Желающие могут модернизировать подпрограмму символьного вывода, расширив таблицу за счет букв русского алфавита, псев- псевдографики и других символов, увеличив тем самым число отобра- отображаемых уровней. Однако это может привести к трудностям при переносе программ. Рассмотрим теперь назначение, параметры и устройство под- подпрограмм форматного вывода полей. Их тексты на языке Си по- помещены в конце данного подраздела. Базовыми являются подпрограммы вывода фрагмента KIOFCW и KIOFDW в символьной и числовой форме соответственно. Эти подпрограммы приведены на листинге 1.11. Подпрограммы имеют сходное устройство и одинаковый на- набор входных параметров. Первым в списке параметров стоит указатель на файл вывода. Для вывода на экран необходимо при обращении указать stdout — стандартный файл вывода Си. Для вывода на магнитный диск или принтер требуется сначала открыть соответствующий файл, указатель на который исполь- использовать в обращении, как это сделано, например, в подпрограм- подпрограмме диалогового управления выводом KIOFEY, показанной на ли- листинге 1.12. Второй параметр — ширина левого поля в знакоме- знакоместах. Этот параметр позволяет получать распечатку на принте- принтере с полем для подшивки; может быть полезен и при выводе на экран. Параметр Image задает имя изображения, Ni, Nj — его раз- размеры. Параметры Nil, Ni2 задают первую и последнюю стро- строку фрагмента, Njl, Nj2 — первый и последний столбец. Если Nil = 1, Njl = 1, то начало фрагмента (т. е. его левый верхний угол) совпадает с началом поля. Параметр wl задает число знако-
1.3. Символьный и числовой вывод Таблица 1.1. 31 Уровень 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Символ • 1 2 3 4 5 6 7 8 9 а Ь с d е f g h • i • J к 1 m Уровень 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 Символ n о Р q г s t u V w X У z A В С D E F G H I J Уровень 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 и выше Символ К L М N О Р Q R S т и V W X Y Z [ \ ] # мест на один элемент поля. Последний параметр, XOY, позволяет задать или подавить оцифровку осей поля. Некоторые параметры проверяются на допустимость. Так, например, если фрагмент ча- частично выходит за пределы поля, то будет выведена только та его часть, что принадлежит полю. Если фрагмент целиком лежит вне поля, произойдет останов с выдачей сообщения об ошибке. Для оцифровки горизонтальной оси служит вспомогательная подпрограмма KIOFAX. Благодаря параметру w\ ее можно исполь- использовать как в KIOFCW, так и в KIOFDW, отличающихся количе- количеством знакомест на один элемент поля. Отметим, что, если с помощью KIOFCW или KIOFDW попы- попытаться вывести слишком большой фрагмент, возможны неприят- неприятности. Какие — попробуйте догадаться сами. Чтобы их избежать, следует разбить поле по ширине и высоте на отдельные страницы.
32 Глава 1. Представление и хранение изображений Листинг 1.11. Функции KIOFAX, KIOFCW, KIOFDW /¦ ФАЙЛ — KIOFAX. С ¦/ ПОДПРОГРАММЫ ДЛЯ ФОРМАТНОГО ВЫВОДА МАТРИЦ */ в указанный файл с регулировкой левого поля */ ПОДПРОГРАММЫ */ оцифровка горизонтальной оси */ символьный вывод фрагмента поля */ числовой вывод фрагмента! поля */ вывод фрагмента поля с диалоговым управлением */ вывод фрагмента поля по страницам */ символьный вывод поля на экран по страницам */ числовой вывод поля на экран по страницам */ указатель на файл вывода */ левое поле, знакомест */ ширина поля вывода (вклвчая левое поле) */ ширина столбца поля на печати, знакомест A...4)*/ vl*l-2 - символьная печать, wl*3-5 - числовая */ строк поля на странице (без учета оцифровки) */ выводимая матрица */ размеры матрицы, строк и столбцов */ начальная и конечная строки фрагмента */ начальный и конечный столбцы фрагмента */ ¦include <stdio.h> finclude "IMAGE.H" void KIOFAX (Fout,left,Ijl,Ij2,?l) FILE *Fout; int /¦ /¦ /¦ /* /¦ /¦ /¦ /¦ /¦ /¦ /¦ /¦ /¦ /¦ /* /¦ /¦ /¦ /¦ KIOFAX KIOFCW KIOFDV KIOFEY KIOFPG KIOTC KIOTD Fout left vid vl lp*g Iaage Ii, Hj Mil, Ii2 Hjl, Hj2 /* Оцифровка, горизонтальной оси */ /ф „ */ { int jj5 j5e(l+(Ijl-l)/5)*5; /¦ первое j, кратное 5, в зоне печати ¦/ if ( J5-4 «« Ijl ) j5-»5; if ( j5 > Ij2 ) j5«Ijl; /¦ очень узкая зона ¦/ fprintf <Fout,"\n%*d", left+6+(j5-Hjl+l)*?l,j5); for (j»j5+5; j<»Ij2; j+»5) fprintf (Fout,"X*dM, 5*wl, j); fprintf (Fout,"\nM); j « Ijl-1; fprintf (Fout/'X^c11, left+б, (j%5)?' ' : «jXW)?'.':'!') );¦ for (j-Ijl; j<»Ij2; j
1.3. Символьный и числовой вывод 33 { for (p*wl-l; p>0; p~) fprintf (Fout,"-"); if <!(jy.S)) fprintf (Fout,"%c", < else fprintf (Fout,"%cJI, »-'); > fprintf (Fout,u\njl); void KIOFCW (routДeft,I«age, li.Ij» Hil,Ii2, Ijl,Ij2, wi.XOY) FILE *Fout; SHOW 1шщ%П; int left,Mi,nj, Hii,Hi2, Hji,Ij2, wl,X0Y; /ф -. — ¦/ /¦ Символьный вывод фрагмента поля */ /* XGY - оцифровка осей: i * да, О ж нет */ • ~ *¦"-•" —— — щ/ { int i?j, u; COUKT k,kO; KIHERS ("KIOFCW:",Ii,l3, tMil,kKi2, kMjl,&Mj2); /* Контроль */ if ( left<0 ) left»O; if ( wKl ) el«l; if ( ?i>2 ) wl=2; if (XOY) KI0FAX(Fout,left,Hji,Ij2,wl); /* Оцифровка гориз.оси */ for (i»fiil, kO=MARK.(Hii,Mji>; i { if (XOY) fprintf (Fout,"X*d •ll,left+4,i>; else fprintf (Fout,"%*ssl, left,11"); for (k«kOr j { u ¦ I»ag if (!u) ; else if (u<10) u+^'O*; /¦ уровни 1... 9 - цифры */ else if (u<36) u*»8T; /* уровни 10...35 - буквы a...z */ else if (u<67) «+«29; /¦ уровни 36...61 - буквы A...Z */ /* уровни 62...66 - знаки [\3~- */ else u-'t'; /* уровни 67 и выше - символ # */ fprintf <Fout,n%*c", wl,u); } fprintf (Fout,lf\n"); } if (XOY) fprintf (Fout,"\nM); void KIOFDW (FoutДeft,Image, Mi,Ijf Iil,Ni2, Ijl,Ij2, wl,X0Y) FILE *Fout; SHOW Image[]; int left,Ii,Ij, Iil»!fi2» Ijl,Ij2, wl,X0Y; I* Числовой вывод фрагмента поля */ /• XOY - оцифровка осей: 1 я да, 0 ж нет */ { int i,j; COUST k»
34 Глава 1. Представление и хранение изображений XINERS ("KIOFDV:",Ii,Wj, *Iil,*Ii2, *Wjl,*Ij2); /¦ Контроль */ if ( left<0 ) left»O; if < ?l<2 ) ?l»2; if ( if (XOY) KI0FAX(Fout,left,Ijl,Ij2,vl); /* Оцифровка гориз.оси ф/ for (i-Iil, r (i-Iil, kO»HARK_(Iil,Ijl); i<»Ii2; i*+, k<H { if (XOY) fprintf (Fout,"X*d !",l«ft+4,i); else fprintf (Fout,"X*s", left,1111); for (k-kO, j»Ij2-Ijl-H; j>0; j—) fprintf (FouVXtd", vl,lug*Dt++] ); fprintf (Fout/'W); if (XOY) fprintf (FouWn11); Листинг 1.12. Функция KIOFEY •include <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIOFEY.С ¦/ «include "KDV.H" void KIOFEY (Image,Hi,Ij) SHOV Iiage[]; int Hi,Ij; /¦ ф/ /* символьный или числовой вывод фрагмента, поля */ /* с диалоговым управлением */ /* Image - выводимая матрица */ /* Hi, Nj - размеры матрицы, строк и столбцов */ /ф ф/ /* Fout - указатель на файл вывода */ /* left * левое поле, знакомест */ /* width - ширина поля вывода (вклвчая левое поле) */ /* wl - ширина столбца поля на печати, знакомест A...4)*/ /* vlsl-2 - символьная печать, wl*3-5 - числовая */ /* lpag - строк поля на странице (без учета оцифровки) */ /* Mil, Ii2 - начальная и конечная строки фрагмента */ /* Ijl, Ij2 - начальный и конечный столбцы фрагмента */ FILE *Fout, «fopenO; char filon[40]; int XOY, U»0; int Ii2max, Fjpm; static int Iil*l, Ii2, fijlsl, Nj2; static int wl*2, width»79, left»0, lpag»20; char axi»'Y\ rpt»'Y\ old-'Y';
1.3. Символьный и числовой вывод 35 Fout * stdout; LBO: KDVIIT ("Вывод: 1-2 - симв., 3-5 - числовой", Jtwl.1,5); XOY-KDVYES ("Оцифровка осей?", It&xi); KDVIIT ("Общая ширина поля вывода width", kwidth,10,256); KDVIIT ("Левое поле left", kieft,0,width/2); KDVIIT ("Строк на странице lp&g"» Jtlpag,2,999); KDVIIT ("Фрагмент со строки Nil11, Jtlil,l;li); Ii2iax*Iil+lpag-l; /¦ шах строка на странице */ (Ii>Ii2iax) ? Ii2iax : li ; KDVIIT ("по строку Ii2", JtIi2,Iil,Ii); KDVIIT ("Co столбца Ijl11, Jtljl,l,lj); Fjpi»(width-left-((X0Y)?6:0))/wl; /¦ иах число столбцов ¦/ Ij2»(Ij>Ijl+Fjpi-l) ? Ijl+Fjpi-1 : Ij ; KDVIIT ("по столбец Ij2", JtIj2,Ijl,Ij); loop < rpt»>Y'; if (?l<»2) KI0FCW(Fout,left,Iiage,Ii,Ij,Iil,Ii2,Ijl,Ij2,?l,X0Y); else KIOFDH(Fout,left,Image,Ii,Ij,Iil,Ii2,Ijl,Ij2,wl,X0Y); if ( U ) { U»0; rpt*'I'; fclose (Fout); Fout»stdout; printf ("Выведено в Xs\n", filon); } loop { if ( JKDVYES ("Вывод повторить?", Jtrpt) ) return; if ( 'KDVYES ("Параметры сохранить?", Jtold) ) goto LBG; KDVIIT ("Куда: 0-экран, l-файл", JtU,O,l); if (U) { printf (" Файл вывода » "); gets (filon); if ( (Fout » fopen (filon, "w") ) ) break; printf ("Файл %в не открывается!\n", filon); U»0; rpt»»I'; } else break; Разбиение должно быть согласовано с размером страницы, харак- характерным для конкретного устройства. Для дисплея это обычно 24 строки по 80 знакомест.
36 Глава 1. Представление и хранение изображений Подпрограмма KIOFPG (листинг 1.13) как раз и является ин- инструментом для страничной печати полей и их фрагментов. Раз- Разбиение производится автоматически в соответствии с указанной при обращении шириной поля вывода и заданным числом строк матрицы на странице. Список параметров KIOFPG отличается от списка параметров KIOFCW и KIOFDW тем, что отсутствует параметр XOY (оси раз- размечаются всегда), но зато добавлены 2 других параметра: width и lpag. Параметр width задает ширину поля вывода, знакомест. В эту величину входит левое поле. Параметр lpag устанавливает чи- число строк поля в странице (именно число строк поля, а не общее число строк печати на странице). Параметр wl определяет число знакомест на один элемент поля и может принимать значения от 2 до 5. Если wl < 2, то используется символьный вывод, если wl > 3 — числовой. Листинг 1.13. Функции KIOFPG, KIOTC, KIOTD #include <stdio.h> finclude "IMAGE.В" /¦ ФАЙЛ -- KIOFPG.С ¦/ finclude "KDV.H" void KIOFPGCFout,left,Image, Ni,Nj,Nil,Ni2,Njl,Nj2, wl,width,lpag) FILE *Fout; SHOW Image []; int left,Mi,Nj, Nil,Mi2, Eji,Hj2, wl,width,lpag; /¦ ¦/ /* Вывод фрагмента поля по страницам: */ /¦ wl*l~2 - символьный, wl*3-5 - числовой */ /* Fout - указатель на файл вывода */ /* left - левое поле, знакомест */ /* width - ширина поля вывода (вклхчая левое поле) */ /¦ wl - ширина столбца поля на печати, знакомест A...4)*/ /¦ wl=l~2 - символьная печать, wl*3-5 - числовая ¦/ /* 1р&? ~ строк поля на странице (без учета оцифровки) */ /* Image - выводимая матрица */ /* Ni, Nj - размеры матрицы, строк и столбцов */ /* Nil, Ni2 - начальная и конечная строки фрагмента */ /* Njl, Nj2 - начальный и конечный столбцы фрагмента */ char int Fj,Fjpm, page,pagmax, last, 11,12, J1,J2;
1.3. Символьный и числовой вывод 37 RIMERS <"KIOFPG:",Ii,Ij, *Iil,*Ii2» *Hjl,*Hj2); /* Контроль */ if (wKl) »1«1; if (wl>5) ?l»5; Fj x MJ2-HJ1+1; /* число выводимых столбцов */ Fjpm ж (width-left-6)/wl; /¦ макс.к-во столбцов в странице*/ if (Fj<=Fjpm) pagmax*l; /¦ выводим в одну полосу */ else { pag*ax*Fj/Fjpm; /* здесь - число полных полос */ last*FjXFjpm; /* сколько столбцов осталось) */ if (last) /* остаток - не нулевой ¦/ { pagaax++; /* общее число полос печати */ if (pagmax*(Fjp*-l)»»Fj) Fjp*--; /* удалось разделить поровну */ else if (pagmax*(Fjpm-2)*»Fj) Fjpm-*2; /* или так - тоже поровну */ else if (last<*2 It* (pagmax-H-lastX^Fjpm) Fjpm—; /* один нли два столбца в последней полосу оставляем */ /* только в том случае, если уменьшение числа столбцов */ /* на странице приведет к увеличения числа страниц */ /* цикл вывода страниц */ for (page*l, Jl*Hjl, J2«HjH-Fjpm-l; page<*pagmax; page++, Jl+*Fjpm, J2+*Fjpm) { if ( J2>Nj2 ) J2*Nj2; /¦ страница меньшей ширины)*/ for <il-Iil9 I2-H4lpag-1; IK*Ii2; Il-»-«lpag, I2+«lpag) { if (I2>Ni2) I2*Ni2; /¦ страница меньшей высоты */ if (wl<*2) KI0FCW(Fout,left,lKage,Ni,KjfIl,I2,Jl,J2,wl,l); else KI0FDW(Fout,left,I»age,Ii,Ij,IlFI2,Jl,J2,wl,l); /* После вывода последней страницы запрос не нужен */ if ( page<pagmax {| Il+lpag<*Ni2 ) if ('КОУУЕЗС'Следуящая страница?", fcrpt)) return; void KIOTC (I»age,Ii,Nj) SHOW Image[]; int Ii,Mj; /* Символьный вывод поля на экран по страницам */ / < KIOFPG (stdout,0, Image,Mi,Rj, l,Ni,l,Kj, 2,79,20); }
38 Глава 1. Представление и хранение изображений void KIOTD (Image,Ii,Nj) SHOV Imaged; int Ii,Hj; /* Числовой вывод поля на экран по страницам */ { KIOFPG (stdout.O, Image,ni.Hj, 1,11,1,Ij, 3,79,20); } На базе KIOFPG сделаны удобные подпрограммы символьного и числового вывода изображений на экран — КЮТС и KIOTD. Обращение к ним предельно упрощено: они имеют только 3 пара- параметра — указатель на поле, число строк и число столбцов. Левое поле принимается равным нулю, страницы соответствуют разме- размерам экрана — 20 строк поля по 79 символов. Эти две функции используются в тестовом примере КЮТ1 (листинг 1.14). Обрати- Обратите внимание — в этой программе в отличие от KIOF0 (листинг 1.15) память под изображение отводится динамически, что замет- заметно сокращает объем загрузочного модуля. Листинг 1.14. Демонстрационная программа КЮТ1 tinclude <8tdio.h> finclude "IMAGE.H" /* ФАЙЛ — KI0T1.C */ finclude "KDV.H" /* Числовой и символьный вывод поля на экран */ /* с автоматическим разбиением на страницы */ «define IMLEN 10000 void main () { static SHOW Image [IMLEN]; int L»«37, Hi=32, Ij»37; char repeat*'Y', cnt*'Y'; puts ("\nKI0Tl: Числовой и символьный вывод поля на экран\п">; do { KDVIIT ("Максимальный уровень поля, Lm", JtLa,O,O); KDVIIT ("Высота поля, строк Ii", Ш, 1.128); KDVINT ("Ширина, поля, столбцов ffj", feIj,O,IMLEN/Hi); KIMGDI (Iiage,Ii,Ij,U);
1.3. Символьный и числовой вывод 39 КЮТС (Image,Ii,Ij); /¦ Символьный вывод */ if OKDVYESCПовторить в числовом формате?",tent)) continue; KIOTD (Image,Ii,Ij); /¦ Числовой вывод */ } vhile (KDVYESO1 Задать новое поле?",*геpeat)); EID. Листинг 1.15. Демонстрационная программа KIOF0 finclude <8tdio.h> «include "IMAGE.Ни /¦ ФАЙЛ — KIOFO.C ¦/ •include "KDV.H" /* Числовой и символьный вывод поля в файл */ /* с диалоговым управлением */ tdefme INLEI 2048 Ўold main () { static SHOW Image [IMLEK]; int Lm«16, Hi»20, Hj»32; char repeat*'Y»; puts ("\nKI0F0: Вырезание фрагмента поля и вывод по выбору\п"); do { KDVIIT ("Высота поля, строк Mi", JtMi, 1,220); KDVINT ("Ширина поля, столбцов tfj", ftHj,O,IMLEH/Hi); KIMGAU (Image,Hi,IIj, ItLm) ; /¦ Внимание! Lm меняется */ KIOFEY (Image,Hi,»j); } while (KDVYESCЗадать новое поле?" ,*repe*t)); EMD. Подпрограмма KIOFPG после вывода каждой страницы (кроме последней) делает запрос на продолжение работы. При выводе на экран это позволяет рассмотреть выведенную страницу, при выво- выводе на принтер — сменить бумагу. Для продолжения достаточно нажать <Enter>. Ввод символа 'N' прекращает дальнейшую вы- выдачу страниц, приводя к выходу из подпрограммы.
40 Глава 1. Представление и хранение изображений Забота о приостановке вывода после последней страницы, что- чтобы изображение никуда не «уехало» и не исчезло с экрана, возла- возлагается на составителя вызывающей программы. Это может пока- показаться неудобством, однако позволяет поместить осмысленный за- запрос о характере дальнейшей работы (как это сделано в программе КЮТ1) сразу же после выхода из KIOFPG или из функций КЮТС и KIOTD, сводящихся к вызовам KIOFPG. При отладке программ нередко требуется просматривать от- отдельные участки изображения, причем заранее неизвестно, какие именно. Для этих целей полезна подпрограмма KIOFEY (листинг 1.12), реализующая вывод фрагмента с диалоговым управлением. Подпрограмма также имеет только 3 параметра: имя поля и его размеры. Недостающие данные (размер поля вывода, число строк в странице, положение фрагмента и необходимость оцифровки осей) вводятся в диалоговом режиме. Диалог организован следующим образом. Программа, запра- запрашивая очередной параметр, предлагает некоторое его значение. Если это значение вам не подходит, то вы можете ввести дру- другое, которое требуется. Если устраивает — достаточно нажать <Enter>. Набирать что-либо на клавиатуре приходится лишь то- тогда, когда программа предлагает не то, что надо. Откуда подпрограмма берет числа, предлагаемые вам в процес- процессе диалога? При первом обращении к ней эти числа определяются самой подпрограммой (т. е. либо задаются в виде начальных зна- значений соответствующих переменных, либо вычисляются). При по- последующих обращениях предлагаемое значение может также вы- вычисляться внутри подпрограммы, но если такие вычисления не предусмотрены, то используется значение, оставшееся от преды- предыдущего обращения. В версии KIOFEY, приведенной на листинге 1,12, первоначально задано: ширина поля вывода 79, левое поле 0, 20 строк поля на странице Фрагмент вырезается та левого верх- верхнего угла поля и 2!е выходит за пределы поля. Подпрограмма KIOFEY позволяет после выдачи ос ля на ъкррн вывести его в таком же формате в любой файл по вашему выбору. Для этого надо утвердительно ответить на запросы «Вывод повто- повторить?» и «Параметры сохранить?», после чего ввести имя файла. Отказ от повторения вывода приведет к выходу из подпрограммы. Обратим внимание на один любопытный момент При транс- трансляции KIOFEY в системе «Турбо-Си» выдается предупреждение на оператор присваивания в условном операторе if ( (Font = fopen (filon,'w')) ) ... Компилятор Microsoft Quick-C и другие системы про^раммироза-
1.3. Символьный и числовой вывод 41 ния на Си никаких ошибок или предупреждений не дают. Опера- Оператор синтаксически правилен, и после компоновки программа par ботает. Автору пришлось изрядно поломать голову, пока в [14, стр. 367] не нашелся ключ к разгадке. Смысл предупреждения в том, что транслятор заставляет еще раз проверить — то ли сдела- сделано, что надо? Может быть, вы хотели использовать операцию == проверки на равенство, а не операцию присваивания = ? Посколь- Поскольку в данном случае требуется именно оператор присваивания, мы вправе игнорировать предупреждение. Если вы все же хотите избавиться ох предупреждения, надо отделить оператор присваивания от условного оператора: Fout = fopen (filon,'w'); if (Fout) ... Описанные подпрограммы находятся в файлах KIOFAX.C, KIOFEY.C, KIOFPG.C. Вспомогательные подпрограммы KDVINT и KDVYES, используемые для организации диалога, помещены в отдельных файлах (листинги 1.16, 1.17). Листинг 1.16. Функция KDVINT #include <8tdlib.h> finclude <stdio.h> /¦ ФАЙЛ — KDVIST.C ¦/ •include "KDV.H" void KDVINT <text,Iv<u%IidsislB&x) char «text; /* Заголовок, до 40 символов */ int *Ivar; /* Вводимая переменная */ int I»in,lma*, /¦ Нижняя и верхняя гр&изщы */ /* Ди&лэговыя вв©д целой первкешюЙ ¦/ /¦ с учътвп предмдущего зи алел к я ¦/ /* Если Iisin >ж 1тшх, контроль гр&ниц откличается */ М-^Ь в» Ш* ШЛ ш» <¦» •» ^т ^т *»Кг *т я» ¦№ ^т вШ *•» в" **ж o"i ¦*& ^В *ь )»s яяш ^т mm ti* <шн ^т Лкх щвг шя imm ш, jhb ^м^ ««> «^ |ш» ^т ^т j«> я» и «^ ^м «н ^^ а* мм ¦» «¦ ^» «¦ лт а» ?¦* ¦¦» яш ^т «¦ ^Ь ж { int iold s <з1?йх; chsx digit[16] ; for ( ; ; ) < printf ("%41.40s %S*i » H,text,*l7ax); g©te (digit); if («digit) >Ivar ¦ atoi (digit); if ( (lasin >» 1*аж) И (*Ivar>*Imin kk *Ivar<*Imax) ) return; *l?ar * iold; printf("%15e Допустимо от %d до %d \nM," ",
42 Глава 1. Представление и хранение изображений Листинг 1.17. Функция KDVYES •include <etring.h> •include <stdio.h> •include "KDV.H" /¦ ФАЙЛ — KDVYES. С ¦/ •define TRUE 1 idefine FALSE 0 int KDVYES (text,Cvar) char «text; char «Cvar; /¦ Сообщение, до 40 символов */ /* Введенный символ */ /¦ /¦ Диалоговый выбор из двух альтернатив */ если введено 'Y' или 'у', п/п возвращает TRUE */ если 'I' или 'п' " FALSE ¦/ { char dig[4]; for ( ; ; ) printf ("%41.40s (Y/I) %c »",t«xt,*CYir); gets (dig); if ( strlen(dig) ) ¦Cvar«dig[0]; if (*Cvar»»'Y' || ¦Cvar»«'y') if (*Cvar»»'I' || ¦cvar—'n') return TRUE; return FALSE; 1.4. Сохранение изображений на магнитном диске Для хранения изображений на диске мы используем два способа записи — символьный и двоичный. Символьный способ подходит для изображений с ограничен- ограниченным диапазоном уровней (до 66). В этом случае изображения ко- кодируются в соответствии с таблицей из предыдущего раздела; для вывода используется описанная там же подпрограмма KIOFCW. Пробелы между символами отсутствуют, оцифровки осей нет. Двоичный способ позволяет хранить изображения, имеющие полный динамический диапазон (от 0 до 255). В начале файла мы записываем ключ, содержащий информа- информацию о способе записи и размерах поля. Ключ гарантирует, что программа чтения сможет прочитать только тот файл, который был создан соответствующей программой записи.
1.4. Сохранение изображений на магнитном диске 43 Символьный способ записи реализуется подпрограммами KINSAV (запись) и KINSIN (чтение), показанными на листингах 1.18 и 1.19 соответственно. Они имеют по три параметра: имя массива и его размеры (число строк и столбцов). Подпрограмма KINSIN может читать только те файлы, которые были записаны KINSAV или созданы с помощью текстового редактора в таком же формате. Обе подпрограммы запрашивают имя файла в диалоговом ре- режиме. При ошибке в наборе имени или если указанный файл не может быть открыт, программа требует повторить ввод. Аварий- Аварийный выход осуществляется по нажатию клавиш <Ctrl><C>. Подпрограмма KINSAV в первой строке файла записывает ключ, содержащий размеры выводимого изображения и имя фай- файла, разделенные запятыми. KINSIN считывает и обрабатывает эти данные. Если ключ в начале файла отсутствует или имеет невер- неверный формат (например, вместо запятых стоят другие символы), то KINSIN прекращает чтение (происходит выход в систему). Если ключ прочитан, то указанные в нем размеры сравниваются с за- заданными и проверяются на допустимость. Если считанные раз- размеры не совпадают с ожидаемыми, но не выходят за допустимые пределы (что проверяется подпрограммой KIMERR), то обработка продолжается. Недопустимые размеры приводят к останову. При вводе поля могут возникать следующие ситуации. Если данных в файле меньше, чем ожидается, то «хвост» изображения заполняется уровнем NODATA, значение которого установлено в головном файле IMAGE.H. Если данных в файле больше, чем может поместиться в мас- массиве, то часть данных будет потеряна. Если указанный при обра- обращении к KINSIN размер изображения по горизонтали Nj не совпа- совпадает с прочитанным значением, произойдет взаимное смещение строк (хорошо известное телезрителям как «нарушение синхрони- синхронизации»). Листинг 1.18. Функция KINSAV •include <stdio.h> •include "IMAGE.H" /* ФАЙЛ — KINSAV.С */ void KIMSAV (Image,Iiflj) SHOW Inage[]; int Mi,Mj; /¦ . . +i /* Символьная запись поля на диск с диалоговым управлением */ /¦ — _ .—_„_ „„ „. , +1
44 Глава 1. Представление и хранение изображений void KDVIHT ( char «text, int *Ivax, int Imin, int Ieax); тоid KIMERR ( char «text, int Hi, int Nj); Toid KIOFCW (FILE *f, int left, SHOW I»age[], int Hi, int Hj, int Nil, int Hi2, int Njl, int Nj2, int vl, int 0); FILE «Fout, ¦fopenQ; char filon [32]; KIMERR("KINSAV: указанM,Hi,Hj); /* - Контроль размеров */ loop { printf ("\n Укажите имя файла вывода >> "); gets (filon); Fout ¦ fopen (filon, w"); if ( Fout ) break; printf ("Файл */,s не открывается'\n",filon); } /* вывод размеров поля и имени файла */ fprintf (Fout, ny.3d ,X3d ,y.s\n", Ni,Nj, filon); KIOFCH (Fout,0, Image,Ni,Hj, l,Ni,l,Nj, 1,0); /* вывод поля */ fclose (Fout); printf ("KINSAV: Поле записано в файл */,s\n", filon); Листинг 1.19. Функция KINSIN •include <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KINSIN.С */ void KINSIN (Ieage,Ni,Nj) SHOW Ieage[]; int Ni,Nj; /¦ Чтение символьного поля из дискового файла */ FILE *Fin, *fopen(); COUNT Pi=O,Pj=O, len, k,Km; register mt c; char film[32], oldname[32j; KIMERR("KINSIN: указан",Ni,Nj); /* - Контроль размеров */ loop { printf ("\n Укажите имя исходного файла » "); gets (filin); Fin = fopen (filin,"rM); if ( Fin ) break; printf ("Файл */.s не найден!\n",filin); } oldname[0]=0;
1.4. Сохранение изображений нэ магнитном диске 45 if (fscanf (Fin, "%d Да Дв11, ftPi, ftPj, oldnaee) »¦ EOF) printf ("KINSIN: нет данных для чтения•\n"); if (Hi != Pi) printf ("Задано Ii*Xd, считано Pi»%d ?\n",Ii,Pi); if (Hj • = Pj) printf ("Задано Hj=Xd, считано Pj*Xd ?\n",Ij,Pj); KIMERR <" KIHSIH: прочитан", Pi,Pj); for (len*Pi*Pj, k*0; len>0 fcfc k<Ke; len—) { /* Пробелы к спец. символы не читаем */ while ( (с else else else else if ( с if ( c3 if ( c: if ( c3 Inage[k++] « getc (Fin)) <s ss > >«>()> * с; . >) kk c<« kt c<* tt c<» •9* : }zy ) 95 : - > с ) с ) с ) с с > ) if ( с »= EOF ) break; - 0; -* 48; -» 87; -* 29; ¦ 67; fclose (Fin); printf (" KINSIN: Поле считано из файла %s\n", filin); while ( к<Кв ) lBage[k++] * NODATA; /¦ заполнение 'хвоста* */ Для демонстрации работы подпрограмм символьного ввода/ вывода служит программа KINS3 (листинг 1.20). В режиме за- записи программа позволяет задать размеры поля, заполнить его те- тестовой картинкой и записать в файл под выбранным именем. В режиме чтения можно прочитать выбранный файл и просмотреть его на экране. Указывая при чтении размеры поля, не совпадаю- совпадающие с записанными в файле, можно наблюдать отмеченные выше эффекты. Листинг 1.20. Демонстрационная программа KINS3 •include <etdio.h> ¦include "IMAGE.H" /* ФАЙЛ — KIMS3.C */ •include "KDV.H" •define IMLEN 4096 /* Символьный ввод и вывод поля в файл */ /* с диалоговым управлением */ void eain () { static SHOW Image[IMLEN]; int Lm*16, Ni=32, Mj=32; int ris2; char repeats>Y';
46 Глава 1. Представление и хранение изображений do { pute ("\n КШЗ - символьный ввод/вывод поля на диск\пм); KDVIMT ("Укажите режим: 1-эапись, 2-чтение", Jtri,1,2); if (ri*«l) { puts (" Генерация тестового поля и запись на диск\п"); KDVIHT ("Высота поля, строк Hi", &Hi,0,128); KDVIHT ("Ширина поля, столбцов Hj11, *Hj,l,IMLEM/Hi); RDVIHT ("Максимальный уровень поля, Lm", fcLm,0,0); KIKGCR (I«age,Hi,Hj,La); KIOTC (Image,Hi,Hj); /* Вывод поля на экран */ KIHSAV (Image,Hi,Hj); /* Вывод поля на диск */ > else { puts (" Чтение поля с диска"); KDVIHT ("Высота поля, строк Ni", fcNi,0,128); KDVIHT ("Ширина поля, столбцов Hj11, *Hj,l ,IMLEH/Hi); KIHSIH (Image,Hi,Hj); /¦ Чтение поля с диска ¦/ KIOTC (Image,Hi,Hj); /¦ Вывод поля на экран */ > > while (KDVYES ("Повторить?",ftrepeat)); EHD. Двоичный ввод/вывод полей на диск реализуется парой под- подпрограмм KINBSV (запись) и KINBIN (чтение), показанных на листинге 1.21. Они устроены аналогично описанным выше под- подпрограммам символьного обмена, но имеют 4 параметра: имя мас- массива, количество элементов в нем и размеры поля (число строк и столбцов). Последние два параметра используются только в целях контроля. Подпрограмма KINBSV записывает в начало файла ключ, со- содержащий несколько контрольных байтов и заданные при обра- обращении размеры поля. При чтении KINBIN контролирует наличие и правильность ключа аналогично тому, как это сделано в под- подпрограмме символьного ввода, но сам ключ содержит другие кон- контрольные символы. Подпрограмма KINBSV возвращает целое число, показывающее долю массива (в процентах), выведенную в файл. При нормальном завершении это число равно 100. Подпрограмма KINBIN возвра- возвращает процент заполнения выходного поля считанными данными. При соответствии заданных размеров поля считанным из ключа и при отсутствии ошибок чтения это число также равно 100. Если данных в файле оказалось недостаточно для заполнения всего по- поля, то возвращаемое значение меньше 100. «Хвост» поля заполня- заполняется при этом уровнем NODATA, который равен здесь 255.
1.4. Сохранение изображений на магнитном диске 47 Подпрограммы KINBSV и KINBIN используются также для ввода/вывода полей, упакованных по алгоритму блочного коди- кодирования. Соответствующие программы описаны в следующем раз- разделе. Для проверки работы подпрограмм двоичного ввода/вывода служит демонстрационная программа KINB2 (листинг 1.22), ана- аналогичная KINS3. Листинг 1.21. Функции KINBIN, KINBSV finclude <8tdio.h> finclude "IMAGE.H" /¦ ФАЙЛ — KIHBIH.C ¦/ fundef NODATA fdefine NODATA MURMAX /¦ Для заполнения "хвоста" ¦/ int KINBIN (Paket,Np,Ni,Nj) SHOW Paket []; int Np; int Ni,Nj; /¦ Массив для ввода поля*/ /¦ Элементов в массиве */ /¦ Контрольные размеры */ /¦ /* /* /¦ Двоичное чтение массива. */ Имя файла ввода указывается в режиме диалога. ¦/ Возвращает - процент заполнения массива Paket. */ ¦/ FILE *Fin, *fopen (); char *Mes x " Несовпадение размеров: задано "; char filon[32]; int RisO,Rj*O; /¦ прочитанные размеры */ long k,kp; loop { printf ("\n Укажите имя файла чтения » "); gets (filon); Fin * fopen (filon, "rb"); if (Fin) break; printf (" Файл %s не найден!\n", filon); /¦ Чтение клича */ if ( fscanf (Fin,11 «. X4d X4d »", *Ri,ftRj) < 2 ) { printf("\n KINBIN: Клич не найден."); STOP. } if (Ni !" Ri) printf("Xsстрок %d, считано Xd\n", Mes,Ni, Ri); if (Nj •* Rj) printf("Хвстолбцов %d, считано %d\n",Mes,Nj, Rj); KIMERRC KINBIN: прочитан", Ri,Rj); /¦ Двоичный ввод поля */ k « kp * fread (Paket,sizeof(SHOW),Np,Fin); if ( k <* 0 ) { puts ("Ошибка ввода.11); STOP. }
48 Глава 1. Представление и хранение изображений fclose (Fin); ehile (k<Mp) P&ket[k++] » I0DATA; /* Заполнение "хвоста" */ return (int) A00L*kp/Kp); int KI1BSV (P*Ju»t»Ip,Ii,Ij) SfiOH P&ket[]; int Ip; int Ii,Ij; /* Поли для вывода ¦/ /* Элементов в поле */ /¦ Контрольные размеры */ /* Двоичная запись поля в дисковый файл, */ /* Имя файла вывода указывается в режиме диалога. */ /* Возвращает - скояыю процентов кода выведено в файл. */ FILE *Fout, *fopen О; char filon[32]; 3ong k; KIHEBRC1 KIIBSV: siaai", loop { printf ("\n Укажмте шкк файла вывода » ") Font « fopesa (filon, "ub"); if (Fout) break; printf (" Файл %в ве открывается!\пи, filon); gets (fiion); } /* Вывод клич& * if ( fprintf(Fout," в. X4d %4d *", »i,»j) « EOF ) { printf("\n KIIBSV: Ошибка вывода кляча.'1); STOP. } k « ferite (Paket,sizeof(SHOW),HptFout); if (k ** EOF) { printf("\n KISBSV: Ошибка вывода/5); STOP. } fclose (fout); printf (" Поле записано в файл %s\n"tfilon); return (int) A00L*k/fip); Листинг 1.22. Демонстрационная программа KINB2 Sinelude <8tdio.h> •include "IMAGE.H" «include "KDV.H" /* ФАЙЛ — KIIB2.C */ •define INLEM 4096 void sain () /* Двоичный ввод и вывод поля в файл */ /* с диалоговым управление!* */
1.5. Сокращенное кодирование 49 static SHOW Image[IMLEM]; int Lm»16, Hi»16, Hj»32; int p, ri*2; char repeat»'Y>; do { puts ("\n KINB2 - двоичный ввод/вывод поля на дискЛп"); KDVIMT ("Укажите режим: 1-запись, 2-чтение", fcri,l,2); if (ri»»l) { puts (" Генерация тестового поля и запись на диск\п"); KDVIHT ("Высота поля, строк Mi", kHi,1,220); KDVIMT ("Ширина поля, столбцов Nj", kHj,O,IMLEH/Hi); KDVIMT ("Максимальный уровень поля, Lm", fcLm,0,0); KIMGCR (Iaage,Hi,Hj,Lm); KIOTO (Image,Ni,Mj); /¦ Вывод поля на экран */ /* Вывод поля на диск */ printf ("В файле - %d%% данных\п", KINBSV (Image,Hi*Hj, Mi,Hj)); } else { puts (" Чтение поля с диска"); KDVIHT ("Высота поля, строк Hi", kHi,1,220); KDVIHT ("Ширина поля, столбцов Hj", fcHj,O,IMLEH/Hi); /¦ Чтение поля с диска */ printf ("Заполнено - %й%% поля\п",р»К1НВ1Н (Image,Hi*Hj, Hi,Hj)); if ( р !« 100 ) PAUSE. KIOTO (Iaage,Hi,Hj); /* Вывод поля на экран */ } } ehile (KDVYES ("Повторить?",krepeat)); EHD. 1.5. Сокращенное кодирование Задача компактного кодирования изображений остается актуаль- актуальной, несмотря на стремительный рост объемов оперативной и дол- долговременной памяти современных ЭВМ. Существует множество алгоритмов сокращения избыточности изображений, обладающих теми или иными достоинствами. Для включения в настоящий па- пакет выбран метод усеченного блочного кодирования (УБК), пред- предложенный в 1979 году американскими учеными [20]. Название отражает тот факт, что изображение разбивается на небольшие прямоугольные куски одинакового размера, называе- называемые блоками. Блоки обрабатываются независимо друг от друга. Этот метод в отличие от большинства других подстраивает па- параметры квантования не под некоторую усредненную характери- характеристику всего изображения, а под локальные особенности в пределах
50 Глава 1. Представление и хранение изображений каждого блока. Это позволяет сохранить мелкие детали изображе- изображений, что особенно важно в задачах обнаружения и распознавания различных объектов. Метод не приводит к размыванию границ, что характерно для некоторых других алгоритмов. Метод УБК сопоставим с большинством известных по эффективности сжатия данных и по объему операций, требуемых для кодирования, но не имеет конкурентов по простоте декодирования, которое, собствен- собственно, вообще не требует каких-либо арифметических операций. Базовый алгоритм УБК устроен следующим образом. Изобра- Изображение, представленное матрицей ||А,;||, разбивается на небольшие прямоугольные блоки размером тх п элементов. Каждый такой блок обрабатывается независимо от других, поэтому опишем ал- алгоритм обработки одного блока. Обработка блока начинается с вычисления порога и двух уров- уровней квантования, затем проводится квантование блока на два уровня, после чего следует упаковка проквантованного блока. Для определения уровней квантования сначала вычисляются два первых выборочных момента — среднее значение С и средний квадрат Е: mxn где суммируются элементы изображения в пределах блока, и дис персия Пороговая величина квантователя d полагается равной средне- среднему С. Верхний а и нижний 6 уровни квантования вычисляются из условия сохранения (приближенно!) первых двух выборочных моментов, что приводит к следующим расчетным формулам: = С + <ту/(р - q)/q, A.7) где р ^ тхп — число элементов блока, q — число элементов блока, превышающих порог d. Квантование проводится по обычному правилу: а, если Aij < d, б, если Ац > где Sij — элементы изображения после квантования. После квантования получается блок, содержащий только уров- уровни а и 6. Нетрудно показать, что среднее значение и средний ква- квадрат исходного и кодированного блоков совпадают. Практически
1.5. Сокращенное кодирование 51 для удобства последующей упаковки вместо а записывается нуль, вместо Ь — единица. Уровни а и 6 запоминаются отдельно. Упаковка состоит в том, что блок, содержащий теперь только нули и единицы, интерпретируется как двоичное число, имеющее m х п разрядов. Это число переписывается в переменную (элемент массива) соответствующей длины. Восстановление закодированного изображения также прово- проводится поблочно и состоит в распаковке и обратной подстановке: вместо нулей записывается нижний уровень квантования, вместо единиц — верхний. Из анализа принципов УБК следует, что степень сжатия ин- информации непосредственно зависит от размеров блока. Чем боль- больше блок, тем больше экономия памяти. Экспериментальные иссле- исследования показывают, что для качественного воспроизведения изо- изображений линейный размер блока должен быть в 3... 5 раз мень- меньше интервала корреляции. Мы используем блоки размером 4x4 элемента, что дает удовлетворительные результаты как по степе- степени сжатия, так и по качеству восстановленного изображения для типичных случаев. Сделанный выбор удобен и с точки зрения программной реализации. Так как исходное изображение имеет до 256 уровней и пред- представлено 8-разрядным двоичным кодом, то для хранения блока размером 4x4 элемента в «натуральном» виде требуется 16 байт. Для хранения того же блока в закодированном виде достаточно 4 байт: 16 бит для бинарного образа блока, что составит 2 байта, и по одному байту для хранения уровней квантования а и 6. Та- Таким образом, достигается 4-кратный выигрыш в объеме требуемой памяти. Описанный выше способ определения верхнего и нижнего уров- уровней квантования по критерию сохранения первых двух выбороч- выборочных моментов не является единственным. В работе [21] проанали- проанализирован ряд других критериев. Отмечается, что критерий должен соответствовать целям последующей обработки изображения и ее конкретным особенностям. Важно, чтобы на этапе кодирования была сохранена информация, к потере которой наиболее чувстви- чувствителен последующий алгоритм. Традиционно в качестве меры сходства берут среднеквадрати- ческое отклонение, которое (с точностью до коэффициента) имеет вид ^B2, A.9) ?? » i где А — блок исходного изображения, 5 — блок, проквантованный
52 Глава 1. Представление и хранение изображений на два уровня. Суммирование, как обычно, проводится в пределах блока. При кодировании решается задача на определение условного экстремума. Необходимо так подобрать порог и два уровня кван- квантования, чтобы минимизировать среднеквадратическое отклоне- отклонение. Алгоритм кодирования по критерию A.9) состоит в следую- следующем. Элементы исходного блока ранжируются в порядке возраста- возрастания. Переобозначим их в виде последовательности Zk, к = 1,2 .. .р (р = гп х п). Для некоторого порога d подсчитывается число q элементов, превышающих этот порог. Тогда уровни квантования определятся из соотношений p~"q 1 р Т Zk. A.10) п — а ^—' а *— * ч к=г ч fc=p~ Правило квантования, как и прежде, имеет вид A.8). В процессе кодирования решается задача на определение услов- условного экстремума (минимума) выражения A.9). Запоминаются со- соответствующие этому минимуму уровни квантования и бинарная матрица. Рассмотрим теперь программы блочного кодирования и деко- декодирования изображений. Тексты программ находятся в файле KIBLOC.C, показанном на листинге 1.23. Листинг 1.23. Функции KIBLOC, KIBCDM, KIBCKO, KIBDEC, KIBORD, KIBOUT /¦ ФАЙЛ KIBLOC.C 05.02.92 ¦/ /¦ ¦/ /¦ ПОДПРОГРАММЫ БЛОЧНОГО КОДИРОВАНИЯ ИЗОБРАЖЕНИЙ */ /* и вывода в файл в упакованном виде */ /¦ ПОДПРОГРАММЫ */ /* KIBCDM - бинарное квантование блока с сохранением М и D */ /* KIBCKO - бинарное квантование блона по минимуму С.К.О. */ /* KIBK0D - блочное кодирование изображения и упаковка */ /* KIBDEK - декодирование упакованного изображения */ /¦ KIBORD - чтение упакованного поля и декодирование ¦/ /¦ KIBOUT - кодирование и запись на диск с диалоговым упр-ем*/ /¦ - ¦/
1.5. Сокращенное кодирование 53 finelude <stdio.h> finclude "IMAGE.H" void KIBCDM (Blk,Nb,LevO,Levl) UPAK Blkd; int Hb; SHOW ¦LevO, ¦Levl; /¦ индексы 0 ... Ib-1 ¦/ Бинарное квантование блока с сохранением двух первых выборочных моментов ¦¦¦ размер блока неограничен ¦¦¦ *** диапазон уровней блока неограничен *** РЕЗУЛЬТАТЫ КОДИРОВАНИЯ Blk - бинарный образ блока (затирает исходные данные) НЬ - число элементов изображения в блоке LevO - нижний уровень квантования Levl - верхний уровень квантования ¦/ /— _ — _ __ ___ ______ _ __ _________________________ */ Щ - ш. _ __ щ I { extern double sqrt (double x); float register e; float disp,pO,pl, sum1,sum2; int k,l,Lprg; int register j; /* вычисление мат. ожидания и дисперсии блока изображения */ suml = sum2 = 0.0; for (j=Nb-l; j>=0; j—) { e=Blk[j]; suml+=e; sum2+=e*e; } suml/=Nb; sum2/=Nb; disp = sum2 - /* бинарное кодирование блока ¦/ k=0; Lprg = (int)(suml+0.5); for (j=Nb-l; j>=0; j—) if (Blk[j]>Lprg) { Blk[j]-1; k++; } else Blk[j]=O; /¦ вычисление уровней квантования LevO, Levl */ pO_pl_suml; if (k) { l=Nb-k; pO-=sqrt(disp*k/l); pl+»sqrt(disp*l/k); } ¦LevO - (SH0W)(p0+0.5); ¦Levl = (SHOW)(pl+0.5); void KIBCKO (Blk,Nb,LevO,Levl) UPAK Blk[]; int Kb; SHOW ¦LevO, ¦Levl; /¦ индексы О ... Mb-1 ¦/
54 Глава 1. Представление и хранение изображений /¦ ¦/ /¦ Бинарное квантование блока по критерия минимума среднеквадратического отклонения между исходным и восстановленным изображением. ¦** размер блока неограничен *** ¦¦* диапазон уровней блока неограничен *** РЕЗУЛЬТАТЫ КОДИРОВАНИЯ Blk - бинарный образ блока (затирает исходные данные) НЬ - число элементов изображения в блоке levO - нижний уровень квантования Levl - верхний уровень квантования */ /¦ ¦/ { float sum1«0.0, sub2*0.0; int Hist[256]; /* для накопления гистограммы блока ¦/ int k,e, pO.pl, Iprg; int register j; for (k*255; k>«0; k—) Hist[k]«0; pO « pi * Blk[O]; for (j«Hb-l; j>«0; j—) { e«Blk[j]; if (e<0 II e>255) continue; Hist[e]++; /¦ накопление гистограммы */ if (e < pO) pO*e; /* поиск минимума в блоке */ if (e > pi) plBe; /¦ поиск максимума в блоке */ 8uml +« е; sum2 +ж (е*е); } Lprg ¦ рО; /* текущее (проверяемое) значение порога*/ if ( pl-pO > 1 ) { int is«0; /¦ количество элементов, которые <ж Lprg */ int bsl«0; /* сумма элементов, которые <Ж Lprg */ int LO,L1; int e0»p0, el«pl; float skorain=sura2; float sko; for (e*eO; e<sel; e++) { if ( !Hist[e] ) continue; /¦ Пустые уровни пропустим ¦/ is +« HistCe]; bsl +« (e*Hist[e]); L0 > bsl/is; if ( Nb»is ) LI > el; else LI « (int) ((suml-bsl)/(Nb-ia) ¦ 0.5); sko*sum2+L0*(is*L0-2*bsl) + Ll*((Hb-is)*Ll-2*Csuml-bsl)); if ( sko<skorain ) { skorain»sko; pO»LO; pl'Ll; Lprg*e; } > } /* бинарное кодирование блока ¦/
1.5. Сокращенное кодирование 55 for (j«Nb-l; j>«0; j—) BlkCj] « (Blk[j]>Lprg) ? 1 : 0; ¦LevO « (SH0V)(p0«K).5); ¦Levl « (SHOW)(pi40.5); KIBKOD (Image,Hi,Nj,Paket,Np,KIBCxx) SHOW Image[]; /* Исходное изображение */ int Ni,Nj; /* Размеры изображения */ UPAK Paket[]; /¦ Упакованное поле */ /¦ индексы: 0 ... Np-1 ¦/ int Np; /* Байтов в упакованном поле */ void OKIBCxxX); /* п/п квантования блока */ /¦ ¦/ /* Блочное кодирование изображения и упаковка. */ /* Размер блока фиксирован: 4*4 элемента. */ /* Если размеры поля не кратны 4, то последние */ /* строки и столбцы (от 1 до 3) не обрабатываются. */ /¦ ¦/ { COUNT k; int i,j, kbi,kbj, т,юр; int Nib=Ni-3, Njb=Nj-3; UPAK Blk[16]; SHOW L0,ll; if ( sizeof(SHOW) !« 1 ) { puts ("\n Программы блочного кодирования работают только"); puts ("с полями, элементы которых нмевт длину 1 байт"); STOP. } /* циклы последовательной выборки блоков */ ар=0; for (kbi«l; kbi<=Hib; kbi+»4) for (kbj*l; kbj<=Hjb; kbj+*4, mp+«4) { if ( юр+3 > Np ) { printf (" KIBKOD: Упакована только часть изображения;\п"); printf (" массив под упакованное поле слишком мал:\п"); printf (" он содержит Xd байт, а требуется %d.\n", Нр, 4*(Ni/4)*(Mj/4) ); PAUSE. return; } /* перенос блока изображения в массив Blk */ k = MARK_(kbi,kbj); ю=0; for (i«l; i<*4; i++, k+»Nj) for (j=0; j<4; j++) Blk [m++]«Image[k+j]; (¦KIBCxx) (Blk,16, kLO,kLl); /* кодирование блока ¦/ /* упаковка результатов кодирования в 4 элемента массива Paket */ i^O; for (m»0; m<*7; m++) { i«i*2+Blk[m]; j«j*2+Blk[m+8]; }
56 Глава 1. Представление и хранение изображений Paket[юр] ¦ L0; Paket [mp+1] Paket[mp+1] « L1; Paket[mp+2] « (UPAK) i; Paket[mp+3] « (UPAK) j; void KIBDEC (Image,Hi,Hj,Paket,Np) SHOW Imaged; /* Восстановленное изображение ¦/ int Hi,Ij; /* Его размеры */ UPAK Paket[]; /¦ Закодированное поле ¦/ /¦ индексы: 0 ... Np-1 ¦/ int Np; /* Байтов в закодированном поле ¦/ /¦ ¦/ /¦ Декодирование упакованного изображения. ¦/ /¦ Размер блока фиксирован: 4*4 элемента. */ /* Если размеры поля не кратны 4, то последние */ /* строки и столбцы (от 1 до 3) остаются пустыми. ¦/ /¦ - ¦/ { COUNT k; int i.j» kbi,kbj, m,mp; int Iib*Ni-3, Njb*Nj-3; UPAK Blk[16]; SHOW 10,11; if (Np > 4*(Ni/4)*(Nj/4)) printf ("\n KIBDEC: Исходных данных больше, чем следует\п"); for (k*Ni*Nj; к>0; --к) Image[к]«HODATA; юр=0; /¦ циклы последовательной выборки блоков ¦/ for <kbi«l; kbi<*Mib; kbi+«4) for (kbj*X; kbj<«Njb; kbj+«4, mp+*4) { if ( mp+3>Np ) { printf (" Данных не хватило на все поле\п"); return; > /¦ распаковка текущего блока */ L0 « Paket[ap]; L1 « Paket[mp+1]; i a PaketCmp423; /* if ( i<0 ) i4«256; - это если */ j » PaketCmp+3]; /* if ( j<0 ) j+»256; UPAK—char */ for (m«7; m>»0; m—) { Blk[m]«i%2; /* перенос распакованного блока в изображение ¦/ k * MARK_(kbi,kbj); ¦«<>; for (i«l; i<*4; i-м-, k+=Mj) for (j*0; j<4; j++) Image [k+j] * (Blk[m++L) ? LI : L0 ;
1.5. Сокращенное кодирование void KIBOUT (Image,Hi,Hj) SHOW Imaged; int Hi,nj; 57 /* /* ¦/ ¦/ ¦/ Блочное кодирование поля с выбором критерия и запись на диск с диалоговым управлением { ИРАК «Рак; int Ip » 4*(Ii/4)*(«j/4), «рв-1; /* Память под закодированное поле */ Рак * malloc (Hp); if (!Pak) { printf("KIBOUT: нет свободных %d байт",Нр); STOP. } KDVINT (мП/п кодирования: 1-KIBCDM, 2-KIBCKO", fcsps,l,2); if ( sps==l ) KIBKOD (Image,Hi,Hj, Pak,Hp, KIBCDH); else KIBKOD (Image,Hi,Hj, Pak,Hp, KIBCKO); while ( ! KIHBSV(Pak,Hp,Hi,Hj) ); free (Pak); /¦ запись на диск */ void KIBORD (Image,Hi,Hj) SHOW Imaged; int Hi,Hj; /¦ -— - ¦/ /¦ Чтение закодированного поля с диска и декодирование */ { UPAK *Рак; int Hp « 4*(Ii/4)*(Hj/4); /* Память под закодированное поле ¦/ Рак ¦ malloc (Hp); if (!Pak) { printf("KIBORD: нет свободных %d байт",Нр); STOP. } while ( ! KIHBIH(Pak,HpfHi,Hj) ); /¦ чтение с диска ¦/ KIBDEC (Image>Hi,Hj,Pak,Hp); free (Pak); /* декодирование поля ¦/ Подпрограмма KIBOUT объединяет в своем составе функции, которые необходимы для кодирования и сохранения закодиро- закодированного изображения на магнитном диске. Подпрограмма име- имеет только три параметра — имя массива, содержащего исходное
58 Глава 1. Представление и хранение изображений изображение, и его размеры. Для кодирования и упаковки вы- вызывается функция KIBKOD, использующая в свою очередь одну из функций кодирования блока — KIBCDM или KIBCKO. Вы- Выбор одного из двух имеющихся в нашем распоряжении способов кодирования блока происходит в диалоговом режиме. Для запи- записи упакованного поля на диск используется описанная в разд. 1.4 функция KINBSV. Функции KIBCDM и KIBCKO, имеющие одинаковый набор па- параметров, реализуют кодирование блока изображения по описан- описанным выше критериям. Исходный блок передается в одномерном массиве Blk. Параметр Nb задает количество элементов блока. Па- Параметры LevO, Levl возвращают нижний и верхний уровни кван- квантования. Бинарный образ блока помещается на место исходного массива, затирая ненужные уже данные. Подчеркнем, что функ- функции кодирования блока сами по себе не обеспечивают сжатия ин- информации; эту задачу решает подпрограмма упаковки KIBKOD, одним из параметров которой является функция кодирования бло- блока. Поэтому функции кодирования сделаны взаимозаменяемыми в том смысле, что имеют одинаковый набор формальных параме- параметров. Работа KIBCDM основана непосредственно на использовании определений A.6-1.8). На рис. 1.8 показано исходное тестовое по- поле, созданное функцией KIMGCR, а на рис. 1.9 — поле, полученное в результате восстановления этого изображения, закодированного функцией KIBCDM. Функция KIBCKO, реализующая критерий минимума средне- квадратического отклонения между исходным и восстановленным изображением, использует алгоритм, имеющий одно непринципи- непринципиальное отличие от вышеописанного: вместо ранжирования элемен- элементов строится и используется гистограмма блока. Если диапазон уровней блока не превышает единицу, то обрабатывать гистограм- гистограмму не требуется — можно сразу переходить к кодированию. В общем же случае осуществляется оптимизация порога, для чего в цикле перебираются уровни от минимального до максимального. На рис. 1.10 показано восстановленное поле (после кодирова- кодирования тестового изображения функцией KIBCKO). Подпрограмма KIBKOD предназначена для компактного коди- кодирования изображений, имеющих диапазон уровней @ .. .255). По- Поступающее на вход изображение Image заданного размера разбива- разбивается на квадратные блоки 4x4 элемента. Блоки один за другим об- обрабатываются выбранной подпрограммой блочного кодирования. Результаты кодирования одного блока упаковываются ипоследова- тельные 4 байта выходного массива Paket. Таким образом, коэф-
.5. Сокращенное кодирование 59 10 15 20 1 1 I 2 • 3 ! 4 ! 5 ! 6 ! 7 ! 8 ! 9 ! 10 ! 11 ! 12 ! 13 ! 14 ! 15 • 16 ! a # 1 1 1 1 . a # 1 1 2 2 2 2 1 1 . # 1 2 2 3 3 3 3 2 2 1 1 2 3 3 4 4 4 4 3 3 2 1 * 1 2 3 3 4 5 5 5 5 4 3 3 2 1 1 2 3 4 5 6 6 6 6 5 4 3 2 1 2 3 4 5 6 7 7 7 7 6 5 4 3 2 1 2 3 4 5 6 7 8 8 7 6 5 4 3 2 1 1 2 3 5 6 7 8 9 9 8 7 6 5 3 2 1 i 1 2 4 5 6 7 9 a a 9 7 6 5 4 2 1 1 2 4 5 6 7 9 a a 9 7 6 5 4 2 1 1 2 3 5 6 7 8 9 9 8 7 6 5 3 2 1 1 2 3 4 5 6 7 8 8 7 6 5 4 3 2 1 2 3 4 5 6 7 7 7 7 6 5 4 3 2 * 1 2 3 4 5 6 6 6 6 5 4 3 2 1 1 2 3 3 4 5 5 5 5 4 3 3 2 1 1 2 3 3 4 4 4 4 3 3 2 1 # 1 2 2 3 3 3 3 2 2 1 # 1 1 2 2 2 2 1 1 . i a # # 1 1 1 1 a Рис. 1.8. Исходное тестовое поле, созданное функцией KIMGCR. Максимальный уровень = 10. ( 2 i 3 ' 4 ! 5 ! 6 7 8 9 10 11 12 ! 13 ! 14 15 16 ) ! 7 # ! 1 ! 1 ! 1 ! 1 ! 1 ! 1 ! 1 i 1 , ! 7 # 1 1 1 1 1 1 1 1 # 1 1 4 4 4 4 1 1 , 7 4 4 4 4 4 4 4 4 7 5 1 1 1 4 5 5 5 5 5 5 5 5 4 1 1 1 1 1 1 4 5 5 5 5 5 5 5 5 4 1 1 1 1 1 4 4 5 5 8 8 8 8 5 5 4 4 1 1 1 1 4 4 5 5 8 8 8 8 5 5 4 4 1 1 2 2 2 5 7 7 7 a a 7 7 7 5 2 2 2 10 i 2 2 5 5 7 7 a a a a 7 7 5 5 2 2 2 2 5 5 7 7 a a a a 7 7 5 5 2 2 2 2 2 5 7 7 7 a a 7 7 7 5 2 2 2 1 1 4 4 5 5 8 8 8 8 5 5 4 4 1 1 1 1 4 4 5 5 8 8 8 8 5 5 4 4 1 1 15 1 1 1 4 5 5 5 5 5 5 5 5 4 1 1 1 1 1 1 4 5 5 5 5 5 5 5 5 4 1 1 1 7 4 4 4 4 4 4 4 4 7 # 1 1 4 4 4 4 1 1 # 1 1 1 1 1 1 1 1 го 7 л l l l l l l l l # 7 Рис. 1.9. Поле, восстановленное после кодирования алгоритмом KIBCDM.
60 Глава 1. Представление и хранение изображений 0 1 2 < 3 < 4 5 б 7 ' 8 9 10 ' И 12 13 14 15 16 1 а i Ш ! 1 » 1 ! 1 ! 1 i 1 ! 1 ! 1 ! 1 ' , ! а 1 1 3 3 3 3 1 1 3 3 3 3 3 3 3 3 а 3 3 3 3 3 3 3 3 а 5 3 3 3 4 4 7 7 7 7 4 4 3 3 3 3 з 3 4 7 7 7 7 7 7 4 3 3 3 3 3 3 7 7 7 7 7 7 7 7 3 3 3 3 3 3 3 7 7 7 7 7 7 7 7 3 3 3 3 1 4 4 4 6 9 9 9 9 9 9 6 4 4 4 1 10 _ i. 1 4 4 <* 4 б 9 9 9 9 9 9 б 4 4 л 4 1 1 4 4 4 б 9 9 9 9 9 9 6 4 4 4 л 1 1 4 4 4 б 9 9 9 9 9 9 6 4 4 4 1 3 3 3 3 7 7 7 7 7 7 7 7 3 3 3 3 3 3 3 7 7 7 7 7 7 7 7 3 3 3 15 3 3 3 4 7 7 7 7 7 7 4 3 3 3 3 3 3 4 4 7 7 7 7 4 4 3 3 3 а 3 3 3 3 3 3 3 3 а л 3 3 3 3 3 3 3 3 , 20 # 1 1 3 3 3 3 1 1 , а а 1 1 1 1 1 1 1 1 , а Рис. 1.10. Поле, восстановленное после кодирования алгоритмом KIBCKO. фициент сжатия информации равен 4 D байта заменяют 16 байт исходного блока). Упаковка состоит в том, что блок, заполненный после кодирования уровнями 0 и 1, переписывается, как двоичное число, в два байта выходного массива. Еще два байта выходного массива занимают нижний и верхний уровни квантования. Отметим, что размеры исходного изображения должны быть кратны 4, чтобы на изображении укладывалось целое число бло- блоков. Если не соблюсти это условие, то правый и/или нижний края поля не будут закодированы, так как обрабатываются только пол- полные блоки. Длина массива Paket, куда записывается закодированное изо- изображение, должна быть достаточной для его размещения. Иначе часть поля не будет упакована. В этом случае произойдет выход из подпрограммы, сопровождающийся соответствующим сообще- сообщением на экране. Для чтения упакованного поля с диска, его распаковки и деко- декодирования служит подпрограмма KIBORD. Чтение с диска осуще- осуществляет описанная в разд. 1.4 функция KINBIN. Для распаковки прочитанных данных и декодирования вызывается подпрограмма KIBDEC. Подпрограмма KIBDEC предназначена для распаковки и деко-
1.5. Сокращенное кодирование 61 дирования изображений, упакованных подпрограммой KIBKOD. Размеры массива Image, в который помещается восстановленное изображение, также должны быть кратны 4. В противном случае правый и/или нижний края поля останутся пустыми. Для ин- индикации подобных случаев массив Image перед началом обработ- обработки заполняется уровнем NODATA, который в публикуемой версии установлен равным 66. Восстановление изображения, как и кодирование, происходит поблочно. Сначала блок распаковывается, и его элементы при- приобретают значения 0 и 1. Затем распакованный блок переносится в массив image, причем вместо значения 0 записывается нижний уровень L0, вместо 1 — верхний уровень L1. При восстановлении необходимо указывать те же размеры по- поля, что были использованы при кодировании. Особенно важно совпадение размера по горизонтали Nj. Его несовпадение приво- приводит к взаимному сдвигу строк изображения. Кроме того, размеры поля должны быть согласованы с размером массива Paket. Если размеры поля велики, то часть поля останется заполненной уров- уровнем NODATA. Если же размеры поля недостаточны, то часть ин- информации будет потеряна. Демонстрационная программа KIBL1 (листинг 1.24) позволяет получить тестовое изображение и закодировать его, а затем восста- восстановить и отобразить на экране в символьной и цифровой формах. Вы можете задать размер изображения и число уровней, а также выбрать один из двух способов кодирования. Листинг 1.24. Демонстрационная программа KIBLl finclude <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIBLl.С */ tinclude "KIB.H" «include "KDV.H" /¦ Блочное кодирование поля */ /¦ и просмотр на экране ¦/ void m&inO SHOW *pict; UPAK *Pak; int Ni^ie, Nj=32; /* индексы меняются от 1 до Нх ¦/ int Mur=16, vika=l; СОШГГ VolPic; char repeat*'IP, dig='Y'; do { puts ("\n\n KIBLi: Блочное кодирование/декодирование поля11); KDVTNT ("Высота поля, строк Ni",ftNi,l,512);
62 Глава 1. Представление и хранение изображений KDVIMT ("Ширина поля, столбцов Nj'\&Nj,0,512); VolPic * (long)Mi ¦ Kj; if ( ( pict * malloc(VolPic) ) *» 0 ) { printf ("\n Нет Xu свободных байт!\п", VolPic); break; } KDVIKT ("Максимальный уровень поля, Mur",kMur,l,MURMAX); KIMGCR (pict,Mi,Mj,Миг); KIOTC (pict,Mi,Nj); if ( ( P&k * malloc(VolPic/4) ) ¦¦ 0 ) { printf ("\n Не хватает У.и 6айт«\п", VolPic/4); break; } KDVINT ("Вид кодирования: 1«КIВСDM, 2»KIBCK0",fcvika,l,2); if (vika*«l) KIBKOD (pict,Mi,Mj, Pak,4*(Mi/4)*(Mj/4), KIBCDM); else KIBKOD (pict,Mi,Mj, Pak,4*(Mi/4)*(Mj/4), KIBCKO); KIBDEC (pict,Mi,Mj,P&k,4*(Mi/4)*(Mj/4)); puts (" Полюбуйтесь на восстановленное изображение"); puts (" Сначала в символьном виде"); KIOTC (pict,Hi,Mj); if (IKDVYES ("Теперь в цифровом формате?", tdig)) continue; KIOTD (pict,Mi,Mj); free (P&k); free (pict); } while (KDVYES ("Повторить?", krepeat)); EMD. Программа KIBL2 (листинг 1.25) аналогична предыдущей, за одним исключением: для отображения полей вместо функций страничного вывода на экран КЮТС и KIOTD используется функ- функция KIOFEY, позволяющая выводить поле не только на экран, но и в любой указанный файл в том же формате. Изображения, поме- помещенные на рис. 1.8—1-10, получены с помощью программы KIBL2: сначала выведены в отдельные файлы, а затем включены в данный текст с помощью экранного редактора. Листинг 1.25. Демонстрационная программа KIBL2 •include <stdio.h> •include "IMAGE.H" /* ФАЙЛ — KIBL2.C ¦/ •include "KIB.H" ¦include "KDV.H"
1,5. Сокращенное кодирование 63 fdefine IMLEN 2048 /¦ Блочное кодирование поля, */ /¦ просмотр на экране и запись */ static SHOW pict[IMLEN]; static UPAK Pak[IMLEN/4]; int Ni*16, Ij*20; /¦ индексы меняются от 1 до Nx */ int Mur^lO, vika=l; char repeat='N'; do { puts ("\n\n KIBL2: Блочное кодирование/декодирование поля"); KDVINT ("Высота поля, строк Hi",fcHi,1,200); KDVIHT ("Ширина поля, столбцов Nj",ftNj,O,IMLEN/Ni); KDVIHT ("Максимальный уровень поля, Mur",ftMur,l,MURMAX); KIMGCR (pict,Ni,Nj,Mur); KIOFEY (pict,Ni,Nj); KDVINT ("Вид кодирования: 1=KIBCDM, 2=KIBCK0",fcvika,l,2); if (vika«l) KIBKOD (pict,Ni,Nj, Pak,4*(Ni/4)*(Nj/4), KIBCDM); else KIBKOD (pict,Ni,Nj, Pak,4*(Ni/4)*(Nj/4), KIBCKO); KIBDEC (pict,Ni,Nj,Pak,4*(Ni/4)*(Nj/4)); puts (" Полюбуйтесь на восстановленное изображение"); KIOFEY (pict,Hi,Hj); } while (KDVYES ("Задать новое поле?", fcrepeat)); END.. Демонстрационная программа KIBL3 (листинг 1.26) служит для проверки алгоритмов кодирования и восстановления совмест- совместно с записью на диск. Сначала выбирается режим: запись или чтение. В режиме записи можно задать размер изображения и число уровней, выбрать способ кодирования и указать имя файла для записи закодированного поля. В режиме чтения вы можете задать размер поля и указать имя файла чтения. Как исходное, так и восстановленное изображения выводятся на экран по стра- страницам.
64 Глава 1. Представление и хранение изображений Листинг 1.26. Демонстрационная программа KIBL3 finclude <etdio.h> •include "IMAGE.Ни /¦ ФАЙЛ — KIBL3.C ¦/ «include "KIB.H" iinclude "KDV.H" /* Блочное кодирование поля, */ /* запись на диск, чтение */ /* и просмотр на экране ¦/ void mainO { SHOW ¦Image; COUMT VolPic; int Ni*16, Nj=32; /¦ индексы меняется от 1 до Nx ¦/ int Mur*16, ri*2; char repeat»'!', dig*'Y>; do { puts ("\n\n KIBL3: Блочное кодирование/декодирование поля"); KDVIIT ("Высота поля, строк Hi",fcHi,1,225); KDVIHT ("Ширина поля, столбцов Hj",*Ij,0,512); VolPic « (long)Mi ¦ Ij; if ( ( Image * ¦alloc(VolPic) ) »* 0 ) { printf ("\n Нет %u свободных байт!\п", VolPic); break; } KDVIIT ("Укажите режим: 1-завись, 2-чтеиие",fcri,l,2); if (ri»»l) { puts ("\n\n Создание поля, кодирование и запись на диск"); KDVINT ("Максимальный уровень поля, MurfI,kMur,l,MURMAX); KIMGCR (Iaage,Hi,Ij,Mur); KIOTO (Image,Ni.Nj); КIBOUT (Image,Ni,Ij); /* Кодирование и запись на диск */ } else { puts ("\n Чтение поля с диска и декодирование"); KIBORD (Image,Ni.Nj); puts (" Полибуйтесь на восстановленное изображение"); puts (" Сначала в символьном виде"); КЮТС (Image fBi,Nj); if (!KDVYES ("Теперь в цифровом формате?", kdig)) continue; KIOTD (Image,Hi.Nj); } free (Image); } while (KDVYES ("Повторить работу?'1, fcrepeat)); EID.
1.5. Сокращенное кодирование 65 Прототипы функций, описанных в гл. 1, содержатся в файле KIMAG.H (листинг 1.27). Не расстраивайтесь, если обнаружите в нем незнакомые вам функции, — они будут описаны в последую- последующих главах. Этот файл подключается к файлу IMAGE.H соответ- соответствующей директивой препроцессора, поэтому в программах его подключать не следует. На листинге 1.28 показан головной файл KIB.H, содержащий прототипы функций, необходимых для блочного кодирования. Этот файл надо подключать директивой #include в программах, использующих блочное кодирование. Бели вы систематически пользуетесь блочным кодированием, то лучше подключить его к файлу IMAGE.H, как мы сделали с файлом KIMAG.H, — тогда не потребуется явно подключать его к программам. На листинге 1.29 показан головной файл KDV.H, содержа- содержащий прототипы ряда вспомогательных функций диалогового вво- ввода/вывода. Две из них уже упомянались нами — это KDVINT и KDVYES. Листинг 1.27. Головной файл KIMAG.H /* ФАЙЛ — KIMAG.H */ /* Прототипы функций пакета */ void KIGLIK ( SHOW Image[], int Ii, int Ij, int, int, int, int, int L); void KIGPNT ( SHOW Imaged, int Ki, int Kj, int, int, int L); Ўaid KIMERR ( char *text, int Ki, int Kj); int KIHGAU ( SHOW Inage[] Ўoid KIHGCR ( SHOW lMage[] Ўoid XIHGDI ( SHOW Imaged Ўoid KIMGSQ ( SHOW Imaged void KIMGTP ( SHOW Image[] Ўoid KIMGUR ( SHOW Imaged, int Hi, int Kj, int Mur); Ўoid KIHGWF ( SHOW ImageD, int Ii, int Ij, int Mur, int Fi, int Fj); void KIHHIS (SHOW Imaged, int Hi, int Ij, int HistQ); void KXHHFR (SHOW Image[], int Ii, int Ij, int lil, int Ii2, int Ijl, int Ij2, int Hist[j); void KIMHMA (SHOW Imaged, int Ii, int Ij, int lil, int Ii2, char Mask[], int Mi, int Mj, int Hist[] , int Histl[]); void KIMSTA (SHOW Imaged, int Ii, int Kj, float Reed); void KIMSTF (SHOW Imaged, int Ii, int Ij, int lil, int Ki2, int Ijl, int Ij2, float Res[]); int int int int int Ii, Ii, Hi, Ii! int Ij, int Ij, int Ij, int Ij, int Ij, int int int int int ¦lv); Mur); Mur); Mur); ul, int u2);
66 Глава 1. Представление и хранение изображений int KIHBIH ( SHOW Paket[], int Hp, int Hi, int Hj); int KIHBSV ( SHOW Paket[], int Hp, int Hi, int Hj); void KIHSAV ( SHOW I«age[], int Hi, int Hj); void KINSIH ( SHOW I«age[], int Hi, int Hj); void KIOFCW (FILE *f, int left, SHOW Inage[], int Hi, int Mj, int Hil, int Mi2, int Hjl, int Hj2, int vl, int 0); void KIOFDW (FILE *f, int left, SHOW Image[], int li, int Ij, int Hil, int Ii2, int Ijl, int Hj2, int vl, int 0); void KIOFEY ( SHOW Inage[], int Ni, int Hj); Ўoid KIOFPG (FILE *f, int left, SHOW Inage[], int Hi, int Nj, int Hil, int Ni2, int Hjl, int Hj2, int vl, int vd, int lp); Ўoid KIOTO ( SHOW Inage[], int Hi, int Hj); Ўoid KIOTD ( SHOW Inage[], int Ni, int Hj); Листинг 1.28. Головной файл KIB.H /¦ ФАЙЛ — KIB.H ¦/ /* Прототипы функций пакета */ Ўoid KIBCDM ( UPAK Blk[], int Mb, SHOW *L0, SHOW *L1); Ўoid KIBCKO ( UPAK Blk[], int Hb, SHOW *L0, SHOW *L1); Ўoid KIBKOD ( SHOW Inage[], int Ni, int Nj, UPAK Blk[], int Nb, void (*KIBCxx)() ); Ўoid KIBDEC ( SHOW Inage[], int Hi, int Hj, UPAK Blk[], int Nb); Ўoid KIBOUT ( SHOW Inage[], int Ni, int Hj); Ўoid KIBORD ( SHOW Inage[], int Ni, int Hj); Листинг 1.29. Головной файл KDV.H /¦ ФАЙЛ — KDV.H ¦/ /* Прототипы функций пакета */ int KDVCHR ( char *text, char *c); Ўoid KDVFLO ( char «text, float *Evar, double Enin, double Emax); Ўoid KDVINT ( char «text, int *Ivar, int Iain, int Inax); int KDVYES ( char *text, char *c);
ВИЗУАЛИЗАЦИЯ ИЗОБРАЖЕНИЙ Высококачественое воспроизведение многоуровневых монохрома- монохроматических изображений на устройствах вывода современных пер- персональных компьютеров — непростая и весьма обширная тема, требующая обстоятельного изучения и достойная отдельной кни- книги. Мы рассмотрим лишь один из многих возможных подходов к решению этой задачи и приведем с необходимыми пояснения- пояснениями соответствующие программы. В литературе [3] упоминаются и другие идеи, которые также могут быть использованы при про- программировании полутонового вывода изображений. 2.1. Полутоновый вывод В предыдущей главе мы рассмотрели представление изображений в числовой и символьной формах. Эти формы вывода незаменимы при отладке алгоритмов, так как передают точное значение уровня каждого пикселя. Вместе с тем должна быть предусмотрена воз- возможность вывода монохромных изображений в форме полутоно- полутоновой картинки, наиболее привычной для зрительного восприятия. Мы рассмотрим здесь метод полутонового вывода, основанный на технике растрового клише, хорошо знакомой нам по газетным иллюстрациям. Вглядитесь внимательно в любую газетную фото- фотографию. Вы увидите, что изображение составлено из множества регулярно расположенных точек (растр). Размер точек определя- определяет степень почернения. На светлых участках точки едва заметны, на серых они увеличиваются в размерах и плотно сливаются на темных. При получении газетных клише фотографии переснимают сквозь специальную сетку для создания растра. В случае цифро- цифровых изображений эта часть работы уже проделана при дискрети- дискретизации исходной картинки в процессе ввода. Осталось сопоставить каждому пикселю небольшой участок экрана, придав ему соответ- соответствующую яркость, — и задача визуализации будет решена. В дисплее ЭВМ в отличие от телевизионного экрана сильно
68 Глава 2. Визуализация изображений ограничены возможности управления яркостью луча — обычно бывает всего три или четыре уровня. Мы будем пользоваться дву- двумя уровнями, когда точка растра либо погашена, либо засвечена. В дальнейшем точку растра (элемент разрешения экрана) будем называть пэлом. Заметим, что последний термин в отличие от пикселя не является общеупотребительным. Для получения многоуровнего изображения по принципу кли- клише каждый пиксель отображается на экране квадратной площад- площадкой размером 2 х 2, 3 х 3 или 4x4 пэла. Яркость пикселя опреде- определяется соотношением числа темных и ярких пэлов на элементар- элементарной площадке клише. Площадка 1x1, когда пиксель представлен единственным пэлом, дает 2 уровня яркости, позволяя верно вос- воспроизводить только бинарные изображения. Площадка 2x2 дает 5 уровней яркости, от 0 до 4, площадка 3x3 — 10 уровней, О1 0 до 9, площадка 4x4 — 17 уровней, от 0 до 16. От величины элементарного клише зависит не только число уровней яркости, но и размеры изображения на экране. На зрительное восприятие изображений заметно влияет та- такая, казалось бы второстепенная деталь, как порядок заполнения клише точками, иначе говоря, зависимость между отображаемым уровнем и расположением ярких пэлов на элементарном клише. На рис. 2.1 показано два примера заполнения клише размером 3x3. Видно, что клише каждого последующего уровня получается из клише предыдущего уровня добавлением одной яркой точки. Такой способ заполнения и такие клише будем называть регуляр- регулярными. Мы пользуемся только регулярными клише, так как для их получения нет необходимости хранить матрицы клише всех уровней, достаточно помнить последовательность добавления яр- ярких точек в клише. 2.2. Программы полутонового вывода на экран Для тонового вывода монохромного изображения на экран пред- предназначены подпрограммы KIPIMA и KIPIMD. Изображение при выводе окружается рамкой. Подпрограмма KIPIMD в отличие от KIPIMA позволяет выводить изображения с поворотом на угол 90, 180 и 270 градусов, или без поворота. Рассмотрим сначала подпро- подпрограмму KIPIMA, а затем укажем, чем от нее отличается KIPIMD. Подпрограмма KIPIMA (листинг 2.1) имеет 10 параметров. Изображение размером Ni x Nj элементов передается в подпро-
2.2. Программы полутонового вывода на экран 69 ••• ••• ••• А • • • •• А • • л. л. » А А А АЛА XXX XXX • • Жш Жш • Жш Жш Жш XX. XXX XXX XXX XXX XXX XXX XXX XXX XXX X XX • •• ••* ••• Жш • • Жш • Л • •• » А • • А • • А • • А • XXX Х.Х Х.Х Х.Х XXX XXX • А* • А • А А • А А • ААА Х.Х XXX XXX XXX XXX Рис. 2.1. А — Линейчатое клиширование. Б — Шахматное клиши- клиширование. грамму в виде одномерного массива Image типа SHOW (пользова- (пользовательский тип, определенный как unsigned char). Длина массива не менее Ni х Nj элементов. Параметры ХО, Y0 задают положение левого верхнего угла картинки в координатах экрана: Y0 — номер строки растра, ХО — номер пэла в строке. Напоминаем, что нача- начало координат расположено в левом верхнем углу экрана, ось ОХ — горизонтальна, OY — вертикальна и направлена сверху вниз. Параметр vid задает тип используемого клише: vid = 0 — шахматное клиширование, vid =1 — линейчатое клиширование, vid = 2 — спиральное клиширование. Размер клише, определяющий число пэлов в представлении пикселя на экране, задается параметром ip. Этот параметр мо- может принимать значения от 1 до 4. Сами клише формируются в подпрограмме KIPGUM, которую мы рассмотрим позднее. Параметр dp определяет просвет между соседними пикселями картинки. Как правило, его следует задавать нулевым. Логический параметр positiv позволяет выводить картинку как в позитивном, так и в негативном изображении.
70 Глава 2. Визуализация изображений Последним параметром является массив Colo, определяющий цвета рамки и картинки. Назначение элементов этого массива комментируется в тексте подпрограммы. Упомянутый массив инициируется в основной программе. При этом его элементам присваиваются некоторые значения, определя- определяющие цвета. Корректируя эти значения, можно изменять цвета в ходе выполнения программы. Для этой цели служит подпрограм- подпрограмма KIPICL (листинг 2.2), которая позволяет выбрать цвета рамки и картинки, ориентируясь на образцы 16 цветов, присутствующие в верхней части экрана. Листинг 2.1. Функция KIPIMA finclude <graphics.h> finclude <stdio.h> /¦ ФАЙЛ — KIPIMA.С ¦/ finclude "IMAGE.H11 iinclude "IIP.H" void KIPIMA (I*age,Mi,H j, X0,Y0, vid,ip,dp, positiv, Colo) SHOW Image []; int li,lj, XO,YO, Ўid,ip,dp, positiv; int Colo[4]; /„ „/ /* ТОНОВЫЙ ВЫВОД МАТРИЦЫ НА ЭКРАН */ /¦ Матрицы клише определяется подпрограммой "KIPGUM". Развертка нормальная, по строкам (матричная). Image - изображение, одномерный массив из Ni*Nj элементов Ii - число строк матрицы (высота) Nj - элементов в строке (ширина) (X0,Y0) - левый верхний угол картинки на экране (без рамки) vid - разновидность клише @ ... 2) ip - размер пикселя, точек растра A ... 4) dp - интервал между пикселями, точек растра positiv - картинка: 0 - негатив, иначе - позитив Со1о[0] - цвет фона рамки Со1о[1] - цвет рамки Colo[2] - цвет фона картинки Со1о[3] - цвет картинки */ register int z;
2.2. Программы полутонового вывода на экран 71 int i,j,*,xp,yp,zur; int xl,xr, yt,yb; int fr ¦ 5; /¦ Ширина, р&нки ¦/ int Mir, Ijr, Ri, Rj; /¦ Рекомендуемые размеры*/ int hm ¦ ip*ip; /¦ Максимальный уровень ¦/ int step * ip+dp; char *clx, *cly; /¦ Указателя на клише ¦/ int gdr ¦ DETECT, gaode, errorcode; int Blesk [ MURMAX+1 ]; KIPJAR (I»age, Mi,Hj, 0,1m, Blesk); /¦ Формир. нелинейности ¦/ KIPGUM (vid,ip, fcclx,fccly); /¦ Выбор клише ¦/ initgraph (fcgdr, fcgaode, ""); errorcode x graphresultC); if (errorcode !¦ grOk) /* an error occurred ¦/ printfC"Graphics error: Xs\nM, grapherrorasgCerrorcode)); STOP. KIPGDE (Nj,Ni,kXO,kYO, get»axx(),get»axy(), fr,ip,dp, z ¦ 2*(dp/2); Ri ¦ step*Kir-dp+z; /¦ Размеры картинки */ Rj ¦ step*Kjr-dp+z; xl « XO; xr « XO+Rj-1; yt » YO; yb ¦ YO+Ri-1; •define BOX(CL,SZ) setcolor(CL);rectangle(xl-SZ,yt-SZ,xr+SZ,yb+SZ) BOX (Colo[0], 1); /¦ Фон ¦/ BOX (Colo[l], 2); /¦ Рамка ¦/ BOX (Colo[0], 3); BOX (Colo[l], 4); BOX ( 0, 5); setfillstyle (l,Colo[2]); /¦ Фон ¦/ bar (xl,yt, xr,yb);
72 Глава 2. Визуализация изображений for ( i«l, yp » Y0+dp/2; i<«Iir; i++, yp ¦¦ step) for ( k«(i-l)*Ij, j«l, xp«X<Hdp/2; j<«Mjr; j-м-, xp ¦» step) { zur ¦ Blesk [ Iaage [к**] ] ; for ( z » (positiv) ? zur-1 : Jm-zur-1; z>*0; z—) putpixel (xp*clx[z], yp*cly[z], Colo[3]); } PAUSE. closegr&phO; Листинг 2.2. Функция KIPICL •include <graphics.h> •include <stdio.h> •include "IMAGE.H" •include "KDV.H" •include "KIP.H" /¦ ФАЙЛ — KIPICL.С */ void /* /¦ /«¦ KIPICL (Colo) int Colo[4]; Colo Colo Colo Colo [0] - [1] • [2] • [3] ¦ ВЫБОР - цвет - цвет - цвет - цвет ЦВЕТА. фона рамки рамки фона картинки картинки */ ¦/ static char q ж 'n'; int gdr » DETECT, gaode, errorcode; if («KDVYESCneeTa изменить?", ftq)) return; initgraph (fcgdr, &g»ode, "F:\TC\BCSI"); errorcode » graphresultO; if (errorcode !¦ grOk) /¦ an error occurred */ { printf ("Graphics error: %s\n", grapherrormsg (errorcode) ); STOP. printf ("\n\n\n");
2.2. Программы полутонового вывода на экран 73 { int у, Ytop«16, Ybot«Ytop+16; int nclr, x; int GXb ¦ getaaxxO; int Shag « (H-GX»)/16, Dx « Shag/5, Lx « Shag-Dx; for (x*Dx/2, nclr«0; nclr<«15; nclr**, x**Shag) { setcolor (nclr); for (y«Ytop; y<=Ybot; у-м-) line (x,y, x+Lx,y); setcolor A5); rectangle (x,Ytop, x*Lx,Ybot); printf (" %2d " ,nclr); } printf ("\n\n"); KDVIMT ("Цвет рамки", ftColo[l], 0Д5); KDVIMT ("Цвет фона рамки11, *Colo[0], 0,15); KDVIMT ("Цвет картинки", ftColo[3], 0,15); KDVIMT ("Цвет фона картинки", ftColo[2], 0,15); closegraphO; } Подпрограммы К1РШ А и KIP1MD обладают способностью кор- корректировать положение и размеры картинки при попытке выхода за пределы экрана (или за пределы заданного поля вывода). Это достигается вызовом подпрограммы KIPGDE (листинг 2.3), кото- которая при необходимости смещает (локально, только внутри подпро- подпрограмм!) начальную точку (X0,Y0). Если картинка по одной или обеим координатам превышает размеры экрана, то смещение на- начальной точки не спасает положения. В таком случае выводится только часть изображения, умещающаяся на экране. Поскольку вывод начинается с элемента Ац, тс это левая верхняя часть изо- изображения. Листинг 2.3. Функции KIPGDE и KIPGUM •include <stdio.h> /¦ ФАЙЛ — KIPGDE.С */ •include "IMAGE.H" •include "KIP.H" Ўoid KIPGDE(Nx,Hy, XO,YO, GXaax,GYmax, ir,ip,dp, Hxrec, Nyrec) int Nx»My, GX»ajc,GYafc.x, fr,ip,dp; mt *XO,*Y0, *Nxrec, *Myrec;
74 Глава 2. Визуализация изображений /* Коррекция положения и размеров поля при выходе за границы экрана ОБРАЩЕНИЕ: без поворота или на 180 град.: KIPGDE (txt, Ij,Hi, fcXO,fcYO, fr,ip,dp, Mfjrec,*Mirec); с поворотом на 90 или 270 град KIPGDE (txt, Hi,Hj, fcXO,fcYO, fr,ip,dp, Wfirec,*lfjrec); fr - ширина рамки ip - размер пикселя, точек растра dp - межпиксельный просвет, точек растра Nyrec - рекомендуемое количество пикселей, которое можно разместить на экране по вертикали Nxrec - рекомендуемое количество пикселей по горизонтали*/ int delt, Rx, Ry; if ( ip<l || dp<0 ) { printf ("KIPGDE: ip « Xd, dp * %d\n", ip,dp); STOP. } delt * 2*(dp/2); /¦ Rx, Ry - Размеры поля без рамки ¦/ ¦Mxrec * Мх; Rx * (ip+dp)*Hx-dp + delt; ¦Нугес «My; Ry = (ip+dp)*Hy-dp + delt; if ( *X0 < fr ) { printf ("XO*y.d >", *X0); printf <•• Xd\n", *XO=f r); } if ( *Y0 < fr ) { printf ("YO*%d >и, *Y0); printf (" %d\nH, *YO=fr); } if ( *Y0+Ry+fr-l > GYmax) { printf ("Поле сдвинуто вниз"); if ( (*YO*GY*ax-Ry-fr+l) < fr ) { «YO^fr; Ry=GY*ax+l-2*fr; ¦Nyrec=(Ry-delt+dp)/(ip+dp); printf(", но помещается только %d строк из Xd\n", *Nyrec,Ny); > else puts (""); if ( *X0+Rx+fr-l > GX*ax) { printf ("Поле сдвинуто влево"); if ( (*X0=GXmax-Rx-fr+l) < fr ) { *X0=fr; Rx=GX«ax+l*2*fr; *Mxrec*(Rx-delt+dp)/(ip+dp); printf(", но помещается только %d столбцов из %d\n",¦Nxrec,Ifx); } else puts ("");
2.2. Программы полутонового вывода на экран 75 void KIPCKJM (vid.ip.clx.cly) int vid,ip; char ¦¦clx,**cly; /¦ ¦/ /¦ /¦ /¦ /¦ Ўid ip clx, cly Выдача клише нужного типоразмера */ тип клише: О-шахматное, 1-линейное, 2-спиральное*/ размер пикселя, точек растра */ ссылки на массивы клише */ static char c2x[4] с2у[4] сЗх[9] сЗу[9] с4х[1б] с4у[16] {0,1, 0,1}, /¦ шахматное {0,1, 1,0}, {1,0,0, 2,2,1, 0,1,2}, {1,0,2, 2,0,0, 1,2,1}, {2,0,3,2, 1,3,0,1, 3,1,0,1, 0,2,3,2}, {3,1,0,1, 0,2,3,2, 1,3,0,1, 2,0,3,2}; ¦/ static char d2x[4] d2y[4] d3x[9] d3y [9] d4x[16] d4y[16] /¦ линейное ¦/ {0,1, 0,1}, {0,0, 1,1}, {0,1,2, 0,1,2, 0,1,2}, {0,0,0, 1,1,1, 2,2,2}, {0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1,2,3}, {0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3}; static char e2x[4] e2y[4] e3x[9] e3y[9] e4x[16] е4у[1б] /* спиральное */ {0,0, 1,1}, {0,1, 1,0}, {1,1,0, 1,2,0, 0,2,2}, {1,0,1, 2,1,0, 2,2,0}, {1,1,2,2, 0,1,3,2, 0,2,3,1, 0,3,3,0}, {1,2,2,1, 1,3,2,0, 2,3,1,0, 3,3,0,0}; if ( ip<l if ( ip>4 switch ( { case case case case case case case case case ip 1: 2: 3: 4: 5: 6: 7: 8: 9: ) ip«l; ) ip*4; ¦ 4^vid ) ¦ clx - ¦ clx « ¦clx * ¦ clx » ¦clx = ¦ clx = c2x; c3x; c4x; d2x; d3x; d4x; if ( vid<0 ) if ( vid>2 ) ¦cly = c2y; ¦cly » c3y; ¦cly * c4y; ¦cly = d2y; ¦cly = d3y; ¦cly = d4y; vid=0; vid=2; break; break; break; break; break; break;
76 Глава 2. Визуализация изображений 10: *clx ¦ «2х; *cly ¦ e2y; break; case 11: *clx ¦ вЗх; *cly * вЗу; break; case 12: *clx ¦ «4х; *cly * в4у; break; default: { printf (M\nKIPGUH - error !\n"); STOP. } Как уже говорилось, количество отображаемых уровней ярко- яркости зависит от размера пикселя. Так, если ip= 3, то пиксель ото- отображается площадкой из 9 пэлов. При этом получается 10 гра- градаций яркости — от 0 до 9. Очевидно, что искомое поле может иметь другое число уровней. Как согласовать между собой диа- диапазон уровней цифрового изображения и диапазон яркостей, вос- воспроизводимый программой тонового вывода? Это достаточно серьезная проблема, и решать ее можно по- разному. Одним из путей является преобразование (выравнива- (выравнивание) гистограммы. Способов выравнивания также существует много. Например, в [1] приводится достаточно совершенный алго- алгоритм выравнивания, в котором анализируется не только собствен- собственно гистограмма, но и окружение каждого элемента изображения, а также применяется рандомизация. Мы взяли за основу другой принцип, позволяющий достичь большего быстродействия. Этот принцип состоит в том, что стро- строится (в общем случае нелинейная) функция, с помощью кото- которой каждому уровню исходного изображения Image сопоставля- сопоставляется уровень яркости, лежащий в заданных пределах. Анализ изображения на предмет построения указанной функции прово- проводится подпрограммой KIPJAR, к рассмотрению которой мы еще вернемся. Выходом этой подпрограммы является массив Blesk, в который записана функция преобразования яркости, заданная таблично. Элементы массива Blesk определяют яркости, соответствующие уровням исходного изображения. Число элементов этого массива равно числу уровней исходного поля, так что каждый элемент от- отвечает определенному уровню. Диапазон значений массива Blesk определяет диапазон уровней преобразованного поля, в нашем слу- случае — диапазон яркостей выводимой картинки. На листинге 2.4 показана демонстрационная программа KIPI1, использующая подпрограмму тонового вывода KIPIMA. Програм- Программа позволяет формировать тестовые изображения, описанные в гл. 1, и наблюдать их на экране дисплея. Примеры тоновых изображений, полученных этой программой, приведены на рис. 2.2-2.4.
Программы полутонового вывода на экран 77 а пет; аааааааааа aaaiiaaaai ааааааваааа aiaiaiiaaa •*< ъььььььььжьььааааааааааа лыььььььыьььаааааааааааааа «мьььььыьаааааааааааааа ьььььььь»ьааааааааа«аааа аа аа ььааааа«аа д&ььььььььаааааааавааааа Рис. 2.2. Диагональное заполнение полей размером 48 х 64 элемен- элементов. Диапазон уровней 0,..., 71. л — клише вида 0 размером 2x2 пэла; б — то же, но с межпиксельным просветом 1 пэл; в — клише вида 0 размером 3x3 пэла; г — то же, но с межпиксельным просветом 1 пэл; д — клише вида 0 размером 4x4 пэла; в — клише вида 2 размером 4x4 пэла.
78 Глава 2. Визуализация изображений VAvKv.O-'^V-'-V-V-v ччччччсс'ююх z;;.;: wvsNvv-y-.v-v-y-y.y-:.¦ ЧЛА.ЧЧЧ»-1.1.1.1.»-1.1... -¦-.._. 44VVS|V' .vs.vs.w.-v-..v. .... AWUV.V.V.V-'.'.'.V.V .V44V44V-V---.. чччччч'-'.v.o.'.'.. ЧЧЧЧ.ЧЧ'.'Л'.'Л'Л.. V4--...-_—.-_ Ч* '-•-'•* •»'-'»".'."•'.'¦' в 4 \ 4 4 S >4\\\\\\ ч ч s v . •, -. % > > > > > > > > %>>>>>>>> ХХХХХХХХХХХХХХХХХ**? . . ч •( s s ч ч ч ч > > > > > s > > >.>ш>.>.>.>.%у.>:>:х>;>:>:>:г.г.т.г.^т.г.г.кг.ккшвшшшшшвтвшттш v ч ч ч •.. ч ч >>>>>>>> 44KxXXXXXXXXXXXXXXXXXRRRfCRRBBBBBBBBa«BBB -ч v v ч >>>>>>>>xxxxxxxxKxxxxxxxxxxxxRRRRRRBBBBKBBaaaaaaa \ •. % ч ч •• \ ч > "> > > > > > > 'xXXXXXX'XXXXXXXXXXXXXXRRKKRRB S. 4 «. 4 4 ч •.-.•. ч >>>>>>>> xxxXXXXXXXXXXXXXXXXXXRRRKRRBBiBiBSBBBBBBB Ч1ЧЧ<»>>>>>>>> XXXXXXXXXXXXXXXXXXXXXftRRRRRBBBBBBBBBaaaSB >> >>.>..>>:х>:ху.у.х>:х7.хг.ххгшш*кштвтв >>>>>>> XXXXXXXXXXXXXXXXXXXXXRICKRRRBBBBBBBBBBBBBB RRR8BBBBBBe ЧХХХХХХХ*Х*РСКК >>> XXXXXXXXXXXXXXXXXXXXXftRRRKRKBBBBffieaeBBBBB • • • • • \ 4 \ 4 \ . . . . \ \ \ \ . . . ч ч ч •. ч . . •. \ \ \ \ \ \4S\\\4\>>>>> .xxxxxxxxxxxxr.xKRRftRKRKBttBgitKaftBaBa \у.>:>:>:>:>:>:>:у.7.7.у.г.?.г.г.г.г.г.г.кшкшш>ш*шш9шавв ххххххххххххкккктш&шя&яш >>>>>>>>. ч ч s .ччччо-ччч>>>>>>>> .\XX>. 7.7.7.7.7.F.r.r.W.r.r. .ч>.х>.хх>. > Рис. 2.2, в, г
' • . S •. '. ". •, Ч 4 S Л А А А А ,< .< А Л ,<'.«>,<V»<«;*V»3T'W>? У У У У У У:\\\ X X >. У . г. .'. ^¦'¦¦¦'¦.'.¦¦.'.V/^V.'A'^.'J.' y?Xyx<?5?«ZS Ч "\\,\ ^. 1'... .s.^'»v.v/'.<f,'5.«; •. "•. X X X X / •. •. s. л л .-¦ s X X X >. i I i « . . « . •••• « , у у у у у mmyyy у у у*ру,у 11+144444 Рис. 2.2, д, е.
80 Глава 2. Визуализация изображений Рис. 2.З. Концентрическое заполнение поля размером 48 х 64 эле- элементов. Диапазон уровней 0, ...,71. Клише вида 0 размером 3x3 пэла. Рис. 2.4. Псевдослучайное коррелированное поле размером 48 х 64 элементов. Диапазон уровней 0,..., 71. Клише вида 0 размером 3x3 пэла.
2.2. Программы полутонового вывода на экран 81 Листинг 2.4. Тестовая программа KIPI1 finclude <stdio.h> /¦ ФАЙЛ ~ KIPI1.C */ finclude "IMAGE.H" finclude "KDV.H" finclude "KIP.H" /¦ Заполнение поля */ /¦ н просмотр на экране */ fdefine CL0 1 /¦ Фон */ fdefine CL1 б /* Рамка */ fdefine CL3 14 /* Картинка */ void Bain О SHOW «Image; COUIT nd; int repeats0; int Ni-48, Nj*64, lv-83; int Color[4] - {CL1,CL1,CLO,CL3}; int ip«3, dp»0, vid»0, posit*l; char newf»'y', newp»'y\ End»'n'; puts ("\n\n\nKIPIl - Заполнение поля н тоновый вывод"); KDVINT ("Позитив - 1, Негатив - 0", *posit,0,l); do { if ( 'repeat 11 KDVYES ("Создать новое поле?", fcnevf) ) { newf - 'n»; KDVINT ("Высота поля, строк Ni", tNi, 1,512); KDVINT ("Ширина поля, столбцов Nj", Wj, 1,512); if (repeat) free (Image); nd » (COUNT) Ni ¦ Nj; if ( '(Image * (SHOW *) malloc(nd)) ) < putsO'HeT места для Image!"); STOP. } KINGAU (Image, Ni,Nj, tlv); } if ( 'repeat || KDVYES ("Параметры изменить?", tnevp) ) { KDVINT ("Размер пикселя ip", tip,1,5); KDVINT ("Межпиксельный просвет dp", tdp,0,10); KDVINT ("Тип клише vid", tvid,0,5); } KIPICL (Color); /¦ Выбор цветов */ KIPIMA (Image, Ni,Nj, 261,90, vid,ip,dp, posit, Color); repeat**; } while ( !KDVYES(aKOH4HTb работу?", tEnd) ); free (Image); END.
82 Глава 2. Визуализация изображений Вспомогательная подпрограмма KIPGUM (листинг 2.3) пред- предназначена для формирования клише, которые затем используются подпрограммами тонового вывода KIPIMA и KIPIMD. Она полу- получает параметры vid и ip, задающие тип и размеры клише, и выдает ссылки на массивы, хранящие клише затребованного типоразме- типоразмера. Определены клише размером от1х1до4х4 элемента, трех типов каждое. При задании клише несуществующего размера или типа происходит исправление ошибки. Для контроля работы подпрограммы KIPGUM служит тесто- тестовая программа KIPG0 (листинг 2.5). Эта программа позволяет в диалоговом режиме задавать входные параметры и просматри- просматривать получающиеся клише примерно в таком виде, как показано на рис 2.1. Листинг 2.5. Тестовая программа KIPG0 fin elude <8tdlib.h> •include <stdio.h> /¦ ФАЙЛ — KIPGO.C ¦/ •include "KDV.H" •include "KIP.H" void O, ip*3; ch«*CLX, *CLY; puts ("\n\nKIPQO - просмотр матриц клише на экране11); И1Ю: KDVIMT ("Тип клише vid",fcvia,0,0); KDVIIT ("Размер пикселя ip",tip,0,0); zhi»ip*ip; KIPGUM <vid,ip, kCLX, fcCLY); for(i«O; i<zhi; i++) printf (" X3d",CLX[i]); puts (" CLX"); for(i»O; i<zhi; i++) printf (" X3dM,CLY[i]); puts (" CLY"); {int lev, u, x,y, yes; puts (""); for (y*ip-l; y>»0; y—) { for (lev»l; lev<»zhi; lev++) { if (lev>l) printf (" "); for (x»0; x<ip; x++)
2.2. Программы полутонового вывода на экран 83 for (u*0; u<l«v; u++) if (CLX[u]«»x *t CLY[u]««y) { y«s*l; break; } printf ("Xc", (yes) ? 'X' : '.'); puts (""); goto НПО; Подпрограмма KIPIMD (листинг 2.6) отличается от описанной выше подпрограммы KIPIMA тем, что позволяет выводить изо- изображение с поворотом на угол, кратный 90°. В связи с этим в ее заголовок добавлен еще один параметр — rtr, задающий поворот. Поворот отсчитывается против часовой стрелки. Угол поворота связан с параметром rtr в соответствии с таблицей, причем задан- заданное значение rtr берется по модулю 4. Значение rtr Поворот, град. 0 0 1 90 2 180 3 270 Скажем несколько слов о порядке прорисовки изображений на экране. Исходная матрица всегда считывается из внутренней па- памяти построчно, начиная с элемента Ац. При отсутствии пово- поворота развертка картинки на экране также происходит построчно, слева направо и сверху вниз. Элемент Ац рисуется в левом верх- верхнем углу. При повороте на 90° элемент Ац оказывается в левом нижнем углу, развертка происходит сверху вниз и слева направо. При повороте на 180° элемент Ац — справа внизу, развертка — справа налево и снизу вверх. При повороте на 270° элемент Ац находится справа вверху, развертка — сверху вниз и справа на- налево. Для проверки подпрограммы KIPIMD служит тестовая про- программа KIPI2 (листинг 2.7), аналогичная программе KIPI1. Листинг 2.6. Функция KIPIMD tinelude <gr&phice.h> •include <stdio.h> /* ФАЙЛ — KIPIMD.С */
84 Глава 2. Визуализация изображений •include "IMAGE.H" •include "KIP.H" void KIPIMD (Iaage,Ii,Ij, X0,Y0, vid,ip,dp, poeitiv, Colo, rtr) SHOW Iaage[]; int Ii,Ij ,X0,Y0,vid,ip,dp,poeitiv,rtr; int Colo[4]; /«, «,/ /¦ ТОНОВЫЙ ВЫВОД МАТРИЦЫ НА ЭКРАН ¦/ Матрица считываетея построчно, но на экран выводится с требуемым поворотом 0, 90, 180 или 270 градусов. rtr - поворот против часовой стрелки, rtr * 90 град. */ /«, «,/ { register int z; int i,j,k,xp,yp,zur; int xl,xr, yt,yb; int fr«5; /¦ Ширина, рамки ¦/ int Iir,Ijr, Ri,Rj; /* Рекомендуемые размеры*/ int ha * ip*ip; /¦ Максимальный уровень ¦/ int step * ip+dp; char *clx, *cly; /¦ Указатели на клише ¦/ int gdr * DETECT, gaode, errorcode; int Blesk [ MURMAX+1 ]; KIPJAR (Iaage, Ni,Nj, 0,ha, Blesk); /* Форм-е нелинейности ¦/ KIPGUM (vid,ip, Jtclx,ftcly); /* Выбор клише */ initgraph (ftgdr# ftgmode, ""); errorcode * gi aphresultO; if (errorcode f= grOk) /¦ an error occurred */ { printfC"Graphics error: %s\n", grapherrorasg(errorcode)); STOP. } if (rtr%*4) printf (" Повернуто на %d градусов\п", rtr«90); /* Поворот на 90 или 270 град. */ if (rtr%2) KIPGDE (Ni,Nj, ftXO,ftYO,getaaxx(),getaaxy(),fr,ip,dp, ftNir,ftNjr); /* Поворот на 0 или 180 град. ¦/ else KIPGDE (Nj,Ni, ftXO,ftYO,getaaxx(),getaaxy(),fr,ip,dp,
2.2. Программы полутонового вывода на экран 85 z * 2*(dp/2); Ri » step*Iir-dp*z; Rj » step*Mjr-dp*z; /¦ Размеры картинки ¦/ xl » XO; yt » YO; xr « XO - 1 + ( (rtr%2) ? Ri : Rj >; yb « YO - 1 + ( (rtr%2) ? Rj : Ri ); •define BOX(CL.SZ) setcolor(CL);rectangle(xl-SZ,yt-SZ,*r+SZ,yb+SZ) BOX (Colo[0], 1); BOX (Colo[l], 2); BOX (Colo[l], 3); BOX (Colo[0], 4); BOX ( 0, 5); /¦ /¦ Фон Рамка ¦/ ¦/ setfilletyle (l,Colo[2]); bar (xl.yt, xrtyb); switch (rtr) case 0: /¦ Фон /¦ Mi /¦ Mj ¦/ /¦ Вывод без поворота */ число строк матрицы (на экране - высота)*/ элементов в строке (на экране - ширина)*/ /* for ( i»l, yp * Y0+dp/2; i<=Mir; for ( k»(i-l)*Mj, j«l, xp«X0+dp/2; j<=Mjr; { zur » Blesk [ Ieage[k++] ]; for ( z ¦ (positiv) ? zur-1 : he-zur-1; z>*0; z~) putpixel (xp+clx[z], yp+cly[z], Colo[3]); } break; , yp +s step) , xp ¦« step) ca.se 1: /* поворот на 90 град, против часовой стрелки /* Mi - ширина матрицы ва экране /¦ Mj - высота матрицы «а экране ¦/ ¦/ ¦/ ¦/ for ( i*l, xpaXO+dp/2; i<»Mir; i++, xp +* step) for ( k»(i-l)*Mj, j=l, yp«Y0+Rj-ip-dp/2; j<*Hjr; j++ { zur » Blesk [ Image[k++] ]; for ( z * (positiv) ? zur-1 : he-zur-1; z>*0; z—) putpixel (xp+clx [z], yp+cly[z], Colo[3]); } break; ca.se 2: /* поворот на 180 град. /¦ Mi - число строк матрицы (на экране /* Mj - элементов в строке (на. экране yp-»step ¦/ */ высота)*/ ширина)¦/
86 Глава 2. Визуализация изображений yp -» step) j++,xp-«etep /¦ for ( i»lt yp»Y0+Ri-ip-dp/2; i<»Iir; for ( k«(i-l)*Ij,j«l,xp«X<HRj-ip-dp/2; { zur * Bleak [ lB«Lge[k++] ]; for ( z * (positiv) ? zur-1 : lui-zur-1; z>*0; z—) putpixel (xp+clx[z], yp*cly[z], Colo[3]>; } break; case 3: /* поворот на 270 град, против часовой стрелки ¦/ /* Mi - ширина матрицы на »кране ¦/ /¦ Ij - высота матрицы иа экране */ /¦ for ( i*l, xp«X0+Ri-ip-dp/2; i<*Iir; i++, xp -» step) for ( k*(i-l)*Ij,j*l,yp«Y0+dp/2; j<»Ijr; j++,yp+«etep ) { zur * Bleek [ Iaage[k++] ]; for ( z * (positiv) ? zur-1 : he-zur-1; z>*0; z—) putpixel (xp+clx[z], yp+cly[z] , Colo[3]); } break; PAUSE. closegraphO; Листинг 2.7. Тестовая программа KIPI2 tinclude <stdio.h> tinclude "IMAGE.H" tinclude "KDV.H" •include "KIP.H" /¦ /* ФАЙЛ — KIPI2.C ¦/ Заполнение поля и просмотр иа экране tdefine tdefine tdefine CLO CL1 CL3 1 6 14 /¦ /¦ /¦ Фон Рамка Картинка ¦/ ¦/ */ void aainO { SHOW *Iaage; COUNT nd; int repeat*0 int Ii*48, int Color[4] lv»63; {CL1,CL1,CLO,CL3}; int ip»3, dp*O, vid*0, poeitsl, rtr*O; char newfe'y», newp^'y', End^'n»;
2.3. Выравнивание гистограммы 87 puts ("\n\n\nKIPI2 - Заполнение поля и тоновый вывод"); KDVIMT ("Позитив * 1, Негатяв * 0", fcposit,0,1); do if ( «repeat || KDVYES ("Создать новое поле?", fcnevf) ) { newf ¦ *n'; KDVIMT ("Высота поля, строк Mi", tMi, 1,512); KDVIMT ("Ширина, поля, столбцов Mj", kMj, 1,512); if (repeat) free (Image); nd * (COUMT) Mi ¦ Mj; if ( !(Image = (SHOW ¦) malloc(nd)) ) { puts("Нет места для Image!"); STOP. } KINGAU (Image, Mi,Mj, felv); KDVIMT ("Поворот rtr @...3)", krtr, 0,0); if ( !repeat || KDVYES ("Параметры изменить?", Jtnewp) ) { KDVINT ("Размер пикселя ip", ftip, 1,5); KDVIMT ("Межпиксельный просвет dp", fcdp, 0,10); KDVIMT ("Тип клише vid", ftvid, 0,5); KIPICL (Color); /¦ Выбор цветов ¦/ KIPIMD (Image, ^Ti,Mj, 261,90, vid,ip,dp, posit, Color, rtr); if ( ++rtr > 3 > rtr -« 4; repeat**; } while ( !KDVYES("Закончить работу?", &End) ); free (Image); END. 2.3. Выравнивание гистограммы Как говорилось выше, необходимо «развязать» между собой диа- диапазон уровней исходного изображения и диапазон уровней, вос- воспроизводимых на экране программами тонового вывода. Для это- этого мы используем функциональное преобразование уровня. Для нахождения требуемой функции служит подпрограмма KIPJAR (листинг 2.8). Искомая функция находится посредством сопоставления гистограммы исходного изображения с гистограм- гистограммой, которую должно иметь преобразованное изображение в иде- идеальном случае. Для получения гистограммы исходного изображе- изображения вызывается подпрограмма KIMHIS. Здесь мы несколько забе- забегаем вперед, так как эта подпрограмма рассматривается только в
Глава 2. Визуализация изображений следующей главе (что не мешает нам воспользоваться ей). В ка- качестве желаемой берется равномерная гистограмма; при необхо- необходимости можно задать любую другую. Функция преобразования получается из условия пошаговой минимизации отклонения ги- гистограммы преобразованного изображения от требуемой формы. Подпрограмма KIPJAR имеет 6 параметров. Это исходное изо- изображение, его размеры, минимальный и максимальный уровень преобразованного поля и массив, в который помещается (в та- табличной форме) найденная функция. Подпрограммы KIPIMA и KIPIMD дают примеры использования полученной функции. Листинг 2,8, Функция KIPJAR tinclude <stdio.h> /* ФАЙЛ — KIPJAR.С ¦/ tinclude "IMAGE.H" •include "KIP.H" void KIPJAR (Image,Mi,Hj, Umin,Umax, Fns) SHOW Imaged; int Mi,Mj, Umin,Umax; int Fns[J; /* Fns [ 0 : MURMAX 3 ¦/ Получение таблично заданной функции Fns[], преобразующей яркость изображения таким образом, чтобы гистограмма преобразованного поля была максимально приближена к равномерной. Беспоисковый локально-оптимальный алгоритм. Image - исходное изображение Mi,Mj - его размеры Umin - требуемый минимальный уровень Umax - требуемый максимальный уровень ?ns - сформированная нелинейность */ { int u,r; int Hold [MURHAX+l]; /* для прежней гистограммы ¦/ int Hdes [MURMAX+1]; /¦ для желаемой гистограммы */ int Derr; unsigned int Vg, SumDes, SumFcts0; if ( Umin < 0 || Umin > Umax 11 Umax > MURMAX ) { printf ("KIPJAR? Umin«Xd, Umax*y.d\n", Umin,Umax); STOP. } KIMHIS (Image,Mi,Mj,Hold); /¦ гистограмма изображения ¦/
2.4. Вывод в условных цветах 89 /¦ Формирование равномерной гистограммы в Hdes[] ¦/ Vg»O; for (u=MURMAX; u>«0; u—) { Vg +* Hold[u]; Hdee[u]»0; } for (u«Ueax; u>«Uein; u—) Vg -* (HdesCu] * Vg/(u-Uein+D); SimDes » Hdee [0] ; for (u*0, r*0; r<*MURMAX; ) /¦ подбор нелинейности */ { Derr * SueDes - SuaFct; if < HoldCr] <» 2*Derr ) { SueFct +» Hold[r]; Fns[r++]*u; } else { Fns[r]*u++; if ( u <¦ Ueax ) Sua»Des +» Hdes[u] ; } for <r«MURMAX; Hold[r]«0; r—) ; for (u»0; Hold[u]*»0; u++) ; if ( r-u > Umax-Uiiin ) { Fns[03*UBin; Fns[r]*Ueax; } 2.4. Вывод в условных цветах Как уже говорилось, полутоновый вывод изображений с помощью клише не исчерпывает всех способов их наглядного представления. Другую возможность дает вывод в условных цветах, когда каждо- каждому отображаемому уровню сопоставляется определенный цвет. Важным частным случаем является монохромный вывод, ис- использующий доступные градации серого: черный, темно-серый, светло-серый и белый цвета. Такой вывод реализует подпрограмма KIPIOB, показанная на листинге 2.9. Эта подпрограмма во многом аналогична подпрограмме KIPIMA. Входные параметры позволя- позволяют задавать размер пикселя и межпиксельный просвет в пэлах, а также получать позитивное или негативное изображение. Список входных параметров отличается от подпрограммы KIPIMA тем, что исключены за ненадобностью два параметра, один из которых управляет типом клише, другой — цветами картинки. В процес- процессе вывода изображение преобразуется к 4-м уровням с помощью нелинейности, формируемой подпрограммой KIPJAR. Листинг 2.9. Функция KIPIOB finclude <grs.phics.h> •include <stdio.h> /* ФАЙЛ — KIPIOB.С */ •include "IMAGE.HM •include "KIP.H"
90 Глава 2. Визуализация изображений fdefine HN 3 /¦ Чясло отображаемых уровней - НМ+1 ¦/ void KIPIOB (Image,Mi,Hj, X0,Y0,ip,dp, positiv) SHOW Image[]; int Ii,Ij, X0,Y0, ip,dp, positiv; // /¦ ТОНОВЫЙ ВЫВОД МАТРИЦЫ НА ЭКРАН В УСЛОВНЫХ ЦВЕТАХ. ¦/ /¦ 4 уровня серого тона. Image - изображение, одномерный массив из Ni*Nj элементов Ni - число строк матрицы (высота) Nj - элементов в строке (ширина) (X0,Y0) - левый верхний угол картинки на экране (без рамки) ip - размер пикселя, точек растра dp - интервал между пикселями, точек растра positiv - картинка: 0 - негатнв, иначе - позитив ¦/ register int z; int i,j,k,xp,yp,zur; int xl,xr, yt,yb; int fr ¦ 5; /¦ Ширина рамки ¦/ int Nir, Njr, Ri, Rj; /¦ Рекомендуемые размеры*/ int step * ip+dp; int gdr * DETECT, gmode, errorcode; int Blesk [ MURMAX+1 ]; int Psevdo [HM+1] * {0,8,7,15}; /¦ Шкала серого ¦/ RIPJAR (Image, Ni,Nj, 0,HN, Blesk); /* Форм-е нелинейности ¦/ initgraph (fcgdr, fcgmode, ""); errorcode * graphresultO; if (errorcode !» grOk) /¦ an error occurred */ { printf ("Graphics error: Xs\n", grapherrormsg(errorcode)); STOP. printf ("Размер экрана: Xd x %d\n", getmaxx()+l,getmaxy()+l); KIPGDE (Hj,Ki,ftXO,ftYO,getmaxx(),getmaxy(), fr,ip,dp, z * 2*(dp/2); Ri s step«Hir-dp+z; /¦ Размеры картинки ¦/ Rj s step*Hjr-dp+z; xl ¦ X0; xr « XO+Rj-i; yt s Y0; yb « YO+Ri-1;
2.4. Вывод в условных цветах 91 •define B0X(CL,SZ> eetcolor(CL);rectangle(xl-SZ,yt-SZ,xr+SZ,yb+SZ) BOX ( 0, 1); /¦ Фон ¦/ BOX ( 15, 2); /¦ Р&ика ¦/ BOX ( 0, 3); BOX ( IS, 4); BOX ( 0, 5); eetfilletyle A,0); /* Фон */ bar (xl,yt, xr,yb); for ( i*l, yp ¦ Y0+dp/2; i<»Hir; i++, yp +* step) for ( k»(i-l)*Mj, j»l, xp*X0+dp/2; j<=Mjr; j++, xp +* step) { zur * Bleek [ Image[k++] ]; eetfilletyle A, Peevdo[(positiv) ? zur : HM-zur]); bar (xp,yp, xp+ip.yp+ip); } PAUSE. closegr&phO; Подпрограмма KIPION (листинг 2.10) позволяет отображать условными цветами до 16 градаций уровня, используя для этого 16 цветов стандартной палитры. Соответствие между уровнями и цветами задается массивом Psevdo. Мы использовали порядок цветов, принятый в палитре IBM PC, за одним исключением. В стандартной палитре чиелу 0 соответствует черный цвет, числу 7 — светлосерый, 8 — темно-серый, 15 — белый. Мы поменяли местами светло-серый и темно-серый цвета. На листинге 2.11 показана тестовая программа KIPI3, которая дает примеры использования вывода в условных цветах. Листинг 2.10. Функция KIPION finelude <gr&phice.h> •include <etdio.h> /* ФАЙЛ — KIPION.С */ finclude "IMAGE.H" •include "KIP.H" idefine HM 15 /* Отображаемые уровни 0 ... HM */
92 Глава 2. Визуализация изображений void KIPIOI (lB&g6,li,Mj, X0,Y0,ip,dp, poeitiv) SHOV lB&ge[]; int li,lj, XO,YO, ip,dp, poeitiv; /* */ /* ТОНОВЫЙ ВЫВОД МАТРИЦЫ НА ЭКРАН В УСЛОВНЫХ ЦВЕТАХ */ /¦ 16 уровней - изображение, одномерный массив нэ Mi*Mj элементов Mi - число строк матрицы (высота.) Мj - элементов в строке (ширина.) (XO,YO) - левый верхний угол картинки на. экране (без рамки) ip - размер пикселя, точек растра. dp - интервал между пикселями, точек растра positiv - картинка: 0 - негатив, иначе - позитив */ register int z; int i,j,k,xp,yp,zur; int xl,xr, yt,yb; int fr * 5; /* Ширина, рамки */ int Mir, Mjr, Ri, Rj; /* Рекомендуемые размеры*/ int step * ip+dp; int gdr ¦ DETECT, gnode, errorcode; int Bleak [ MURMAX+1 ]; /¦ Последовательность псевдоцветов ¦/ int Psevdo[HM+l]«{0,l,2,3,4,5,6,8, 7,9,10,11,12,13,14,15}; KIPJAR (Ieage, Mi,Mj, O,HM, Bleak); /* Форм-е нелинейности ¦/ initgraph (ftgdr, ftgeode, ""); errorcode * graphresultO; if (errorcode !¦ grOk) /¦ an error occurred */ { printf ("Graphics error: %в\пи, grapherroresg(errorcode)); STOP. KIPGDE (Mj,Mi,JtXO,JtYO, getmajcxO ,getea.xy(), fr,ip,dp, kNjr,kNir); z ¦ 2*(dp/2); Ri ¦ step*Hir-dp+z; /* Ра.змеры картинки */ Rj « step*Mjr-dp+z; xl ¦ XO; xr « XO+Rj-1; yt » Y0; yb « YO+Ri-1;
2.4. Вывод в условных цветах 93 •define B0X(CL,SZ) setcolor(CL);r«ctangle(xl-SZ,yt-SZ,xr+SZ,yb+SZ) BOX ( 0, 1); /* Фон */ BOX ( 15, 2); /* Рамка */ BOX ( 0, 3); BOX ( 15, 4); BOX ( 0, 5); setfillstyle A,0); /* Фон */ bar (xl,yt, xr,yb); ip—; for ( i*l, yp » Y0+dp/2; i<*Mir; i++, yp +» step) for ( k*(i-l)*Mj, j»l, xp»X0+dp/2; j<*Mjr; j++, xp +¦ etep) { zur » Bleak [ I«age[k++] ]; setfillstyle (l, P»«vdo[(positiv) ? zur : HM-zur3); bar (xp,yp, xp+ip,yp+ip); > PAUSE. closegraphO; Листинг 2.11» Тестовая программа KIPI3 •include <stdio.h> /¦ ФАЙЛ — KIPI3.C ¦/ •include "IMAGE.H" •include "KDV.H" •include "KIP.H" /¦ Заполнение поля */ /¦ н просмотр на экране */ void aainO SHOV *Iaage; COUMT nd; int repeat»0; int Mi«48, Mj*64, lv«83; int ip*3, dp»O, poeit*l; char newfa'y', newpa>y', End^'n', piob^y', pione'y'; puts ("\n\n\nKIPI3 - Заполнение поля и вывод в псевдоцветах"); KDVIMT ("Позитив » 1, Негатив « 0", *poeit,0,l); do if ( «repeat 11 KDVYES ("Создать новое поле?", fcnewf) ) { newf * 'n'; KDVIMT ("Высота поля, строк Mi", *Mi, 1,512); KDVIMT ("Ширина поля, столбцов Mj", *Mj, 1,512);
94 Глава 2. Визуализация изображений if (repeat) free (Iaage); nd ¦ (COUIT) li ¦ Hj; if ( Ullage » (SHOV *) Malloc(nd)) ) { puts ("Нет места, для Iaage!"); STOP. } XIHGAU (Ieage, Hi,Hj, felv); } if ( !repeat || XDVYES ("Параметры изменить?11, ftnewp) ) { KDVIIT ("Размер пикселя ip", tip,0,0); KDVIHT ("Межпнксельный просвет dp11, ftdp,O,O); } if ( KDVYES("Вывести в 4 уровня серого?", ftpiob) ) KIPIOB (Iaage, Hi,Hj, 261,90, ip,dp, posit); if ( KDVYESC"Вывести в псевдоцветах?", ftpion) ) KIPION (Image, Hi,Hj, 261,90, ip,dp, posit); repeat**; } while ( !XDVYES("Закончить работу?", *End) ); free (Ia&ge); EHD. На листинге 2.12 приведен головной файл К1Р.Н, содержащий прототипы описанных в данной главе подпрограмм графического вывода. Листинг 2.12. Головной файл KIP.H /¦ ФАЙЛ — KIP.H ¦/ void KIPGDE (int Их, int Ну, int *X0, int *Y0, int gx, int gy, int fr, int ip, int dp, int *Hxr, int *lyr); void KIPGUM (int vid, int ip, char **clx, char **cly); void KIPICL (int Colo[4]); void KIPIHA (SHOW Ieage[], int Ii,int Ij, int X0, int Y0, int vid, int ip, int dp, int pos, int Colo[4]); void KIPIHD (SHOW Imaged, int Hi,int Hj, int X0, int Y0, int vid, int ip, int dp, int pos, int Colo[4], int rtr); void KIPIOB (SHOW Imaged, int Hi,int Hj, int X0, int Y0, int ip, int dp, int pos); void KIPIOH (SHOW Isage[], int Hi,int Hj, int X0, int Y0, int ip, int dp, int pos); void KIPJAR (SHOW IeageG, int Hi,int Hj, int, int, int Fns[]);
ЭЛЕМЕНТАРНЫЙ АНАЛИЗ ИЗОБРАЖЕНИЙ В данной главе рассматриваются распространенные методы ана- анализа и измерения ряда характеристик изображений, таких, как среднее значение, среднеквадратическое отклонение и некоторые другие. Сюда же мы относим и гистограммы, дающие приближен- приближенную оценку законов распределения. Сначала излагаются необхо- необходимые теоретические сведения, i затем приводятся соответствую- соответствующие программы анализа. 3.1. Задачи статистического анализа В задачах обработки изображений последние нередко интерпре- интерпретируются как случайные процессы двух переменных, т. е. как случайные поля. Это оправдано хотя бы потому, что при форми- формировании изображений практически всегда имеются шумы. След- Следствием указанной интерпретации является то, что для обработки изображений могут и в ряде случаев удачно применяются стати- статистические методы обработки информации. Это различные методы улучшения изображений (подавление шумов, медианная фильтрация и т. д,) [5-7], методы простран- пространственной реставрации (винеровская фильтрация, реставрация ме- методом рекурсивной фильтрации и т. д.) [5, 6, 9, 22]. Стати- Статистические методы позволяют теоретически рассчитывать эффек- эффективность некоторых процедур обработки бинарных изображений [23-25]. Итак, при статистической интерпретации дискретные изобра- изображения рассматриваются как реализации случайного поля, которо- которому (полю) присущи те или иные вероятностные характеристики. Это в первую очередь совместный двумерный закон распределе- распределения вероятностей, который позволяет теоретически рассчитывать корреляционные функции изображения, необходимые, в частно- частности, для синтеза винеровских фильтров [22].
96 Глава 3. Элементарный анализ изображений Для задач обработки изображений наиболее характерен случай, когда имеется конкретная реализация \\Aij\\ дискретного изобра- изображения, а совместный двумерный закон распределения вероятно- вероятностей неизвестен. В ряде задач указанный закон распределения .не- .необходимо оценить по данной реализации. Такая оценка получила название гистограммных признаков второго порядка [5]. Часто дискретное изображение рассматривается как специ- специфическое (а именно, двумерное) представление одномерного слу- случайного процесса, по данным которого и производят оценивание некоторых вероятностных характеристик. Это оценивание од- одномерного закона распределения вероятностей (получение гисто- гистограммы), иногда называемого гистограммным признаком первого порядка (в отличие от гистограммных признаков второго и бо- более высокого порядков). Это также оценивание различных мо- моментов, характеризующих обрабатываемое изображение, причем оценивание указанных моментов может производиться как не- непосредственно по имеющейся реализации изображения, так и с помощью предварительно полученной гистограммы. И наконец, могут быть оценены значения экстремальных статистик (мини- (минимум, максимум), размах [26] и энтропия обрабатываемого масси- массива. Энтропия может быть использована либо как инструментарий для введения различных мер количества информации в изобра- изображениях [9], либо как мера «хаотичности» (в том числе и отно- относительная при соответствующей нормировке) данного изображе- изображения. 3.2. Числовые характеристики изображений Как уже говорилось, изображения могут рассматриваться как ре- реализации некоторого двумерного случайного поля, свойства кото- которого априори обычно неизвестны. Целью статистического анализа изображений является измерение, или, как принято говорить, по- получение оценок указанных характеристик. Соответствующий ап- аппарат хорошо разработан в математической статистике и теории вероятностей [18, 26]. Мы предполагаем, что читатель знаком с основными положениями названных дисциплин. Среди множества числовых характеристик изображений, рас- рассматриваемых как реализации случайного поля, мы выделим не- несколько наиболее употребительных. Это математическое ожида- ожидание (среднее), среднеквадратическое отклонение, коэффициенты асимметрии и эксцесса, энтропия, значения минимального и мак- максимального элементов анализируемого поля и размах (диапазон
3.2. Числовые характеристики изображений 97 уровней). Подчеркнем, что здесь и далее в данной главе речь идет об оценках соответствующих характеристик. Определение статистических характеристик начнем с вычисле- вычисления начальных и центральных моментов. Момент, рассматрива- рассматриваемый относительно начала координат, называется начальным, а относительно математического ожидания — центральным. В теории вероятностей начальные моменты m* Jb-ro порядка вычисляются по общей формуле где ж,- — некоторое значение дискретной случайной величины X, Pi = Р{Х = ж*} -— вероятность, с которой случайная величина X принимает значение ж,-. Математическая статистика оперирует с оценками указанных моментов. Применительно к анализу изображения ||j4ij|| размером Nt х Nj элементов соответствующая формула приобретает вид: В терминах математической статистики множество пикселей, составляющих изображение, являются выборкой. Количество ис- использованных для анализа пикселей мы будем называть объемом выборки (здесь — JVS x Nj). Начальный момент первого порядка mi называется матема- математическим ожиданием, или средним; начальный момент второго порядка ш2 — средним квадратом; моменты третьего и четверто- четвертого порядков используются для вычисления описанных ниже ко- коэффициентов асимметрии и эксцесса. Отметим, что статистический анализ (вычисление моментов, формирование гистограмм и т. п.) может быть выполнен не толь- только для изображения в целом, но и для любого заданного фрагмен- фрагмента. Существуют и более изощренные приемы. Так, в ряде случаев используется так называемый «анализ по маске», когда обраба- обрабатываются элементы поля, образующие заданную маской конфи- конфигурацию. Анализ по маске позволяет определять статистические свойства отдельных объектов изображения, выделенных известны- известными методами [5-7, 9], а также произвольно заданных областей. Его программная реализация не встречает серьезных затруднений. В разд. 3.5 описгна программа для получения гистограммы по за- заданной маске.
98 Глава 3. Элементарный анализ изображений Учитывая сказанное, перепишем выражение C.2), не указывая явно границы суммирования: __ 1 sum где sum — количество обработанных пикселей исходного изобра- изображения. Для вычисления начальных моментов можно использовать не только само изображение, но и его гистограмму. Мы рассмотрим случай, когда ширина интервалов гистограммы равна единице и каждому уровню поля (а их, как правило, 256) соответствует свой столбец гистограммы. В этом случае используется расчетная фор- формула, являющаяся аналогом C.1), га* = — Т h"Gh, C.4) Cilffl шшттшт где h — текущий уровень элементов поля, hmdLX — максимальный уровень поля, Gh — высота столбца гистограммы, т. е. количество элементов изображения, имеющих уровень Л, sum — количество пикселей, использованных при построении гистограммы (объем выборки). Очевидно, что sum = hzzO Гистограмма с единичным интервалом сохраняет полную ин- информацию о моментах распределения, поэтому оценки, получен- полученные по формулам C.3) и C.4), совпадают (если, конечно, анализи- анализируется один и тот же участок). Если ширина интервалов гистограммы больше единицы, то происходит группировка и в один интервал «сливаются» несколь- несколько уровней поля. По такой гистограмме можно получить только приближенную оценку моментов. Центральные моменты г/* в теории вероятностей определяются выражением ик = Понятно, что для практического применения приведенной вы- выше формулы необходимо предварительно оценить среднее значение Иными словами, в программе потребуется дважды перебирать
3.2. Числовые характеристики изображений 99 в цикле элементы поля — первый раз для нахождения среднего, второй раз — для вычисления центральных моментов. Поэтому для получения центральных моментов лучше внача- вначале вычислить начальные моменты по одной из вышеприведенных формул, а затем использовать известные выражения для пересчета начальных моментов в центральные: 1*2 = tx3 = шз — 3mim2 + 2т\ C.5) Центральный момент второго порядка ti2 называется дисперси- дисперсией. Величина сг = называется среднеквадратинеским отклонением. С центральным моментом третьего порядка щ связан коэффи- коэффициент асимметрии д\, характеризующий «скошенность» распре- распределения вероятностей: Л = % C.6) Для симметричного (относительно математического ожидания) распределения коэффициент асимметрии равен нулю. С центральным моментом четвертого порядка гц связан коэф- коэффициент эксцесса </2> характеризующий «крутость» распределе- распределения: 92 = % - 3. C.7) Коэффициент эксцесса нормального распределения равен ну- нулю. Бели кривая плотности вероятностей имеет более острую и высокую вершину по сравнению с нормальным распределением, то эксцесс положителен, если более низкую и пологую — отри- отрицателен. За подробностями мы отсылаем читателей к литературе [26]. Удобной мерой, характеризующей поведение случайной величи- величины (через ее закон распределения) от строгой детерминированно- детерминированности до полной «хаотичности», является энтропия. Ее применение особенно полезно в случаях асимметричных и/или многовершин- многовершинных распределений, когда использование таких числовых характе- характеристик, как среднее значение, среднеквадратическое отклонение и моменты высших порядков, теряет всякую наглядность.
100 Глава 3. Элементарный анализ изображений Энтропия дискретной случайной величины, она же средняя собственная информация, определяется известным из теории ин- информации выражением [18]: ^- C.8) где pi — как и раньше, вероятность, с которой случайная величина X принимает значение ж,-. Энтропия изображения зависит от количества уровней, а при одинаковом числе уровней — от закона распределения. При равно- равномерном законе распределения (полная «хаотичность») энтропия достигает максимума, который зависит только от количества уров- уровней: 10 = Iog2(/imax - /bun + 1). C.9) Так, например, энтропия изображения, имеющего уровни от 0 до 255, не может превышать 8. Степень близости закона распределения к равномерному удоб- удобно характеризовать относительной энтропией I/Iq или величи- величиной D = 1 - J/Jo, (ЗЛО) которая в теории информации называется избыточностью [18]. Мы будем использовать второе понятие. При равномерном законе распределения избыточность равна нулю. Энтропию удобнее всего вычислять с помощью гистограммы. Подставляя в C.8) вместо вероятности р, ее оценку G^/sum и про- проводя элементарные преобразования, получаем = Iog2 sum ]Р Gh log2 Gh. л=о Так как среди стандартных функций библиотеки Си нет двоич- двоичных логарифмов, переходим к натуральным. Расчетная формула приобретает вид max lnsum — У GhlnGh . C.11) sum ^—' / /»=о Перед тем как приступить к рассмотрению программной реа- реализации описанных выше статистических методов приведем пере- перечень подпрограмм данной главы:
3.3. Подпрограммы статистического анализа изображений 101 KHISAN — вычисление статистических характеристик изобра- изображения по гистограмме, KHISTA — построение гистограммы из алфавитно-цифровых символов, KHISTO — то же, с относительной оцифровкой, KHISTW — интерактивное управление выводом гистограммы и статистических характеристик на экран или в ука- указанный файл, KIMHIS — получение гистограммы изображения, KIMHFR — то же, для фрагмента изображения, KIMHMA — то же, по маске, KIMMSM — создание прямоуголы ->й маски, KIMSTA — статистические характеристики изображения непо- непосредственно, KIMSTF — то же, для фрагмента изображения, KOSMOM — пересчет начальных моментов в центральные, KOSPTF — вывод статистических характеристик на экран или в файл в строку (для таблиц), KOSPUF — то же, в столбец. Листинги программ помещены ниже. 3.3. Подпрограммы статистического анализа изображений Подпрограмма KIMSTA предназначена для прямого расчета ста- статистических характеристик изображения, без посредничества ги- гистограмм. Ее входными параметрами являются анализируемое изображение и его размеры. Подпрограмма KIMSTF выполняет ту же функцию для прямоугольного фрагмента изображения. К ее входным параметрам добавлены еще четыре, определяющие гра- границы обрабатываемого фрагмента по строкам и столбцам. Обе подпрограммы приведены на листинге 3.1. Вычисления, как обычно, проводятся в две стадии. Снача- Сначала в соответствии с выражениями C.2) или C.3) находятся на- начальные моменты. В том же цикле определяются минимальное и максимальное значения изображения. Затем с помощью функ- функции KOSMOM вычисляются центральные моменты, коэффициен- коэффициенты асимметрии и эксцесса. Результаты помещаются в массив Res,
102 Глава 3. Элементарный анализ изображений состоящий из 10 элементов типа float. Этот массив является по- последним параметром обеих подпрограмм. Элементы массива Res имеют следующий смысл: Res[0] — количество фактически обработанных элементов поля (объем выборки), Res[l] — среднее значение, Res[2] — среднеквадратическое отклонение, Res[3] — коэффициент асимметрии, Res[4] — коэффициент эксцесса, Res[5] — минимальное значение, Res[6] — максимальное значение, Res[7] — энтропия, Res[8] — избыточность, Res[9] — размах. Подпрограммы KIMSTA и KIMSTF не определяют энтропию и связанную с ней избыточность и поэтому записывают нуль в элементы Res [7] и Res [8]. Энтропия и избыточность вычисляют- вычисляются подпрограммой KHISAN, проводящей анализ гистограмм (см. разд. 3.6). Листинг 3.1. Функции KIMSTA, KIMSTF •include <stdio.h> •include "IMAGE.H" •include "KOST.H" /¦ ФАЙЛ — KIMSTA.С ¦/ /¦ < void XIMSTA (Image,Ni,Nj,Res) SHOW Imaged; int Hi,Mj; float Res [] ; /¦ /¦ Image [0... /* Число элементов Статистические характеристики Res Re 8 Res Re 8 Res CO] [1] C2] C3] [4] - объем выборки - среднее - сигма - асимметрия - эксцесс Если Res Re 8 Res Res [5] Сб] [9] [2] изображения - минимум - максимум - размах =0, то Res[3]«Res[4j»0 SHOW Lmin,Lnax,Lv; unsigned int Ne * Ii*Nj, k float a,b,c,d,yy,zz; KIMERR (MKIMSTA:",Mi,Mj); Nm;
3.3. Подпрограммы статистического анализа изображений 103 a«b« c«d«0.0; Lain * Lmax * Image[1]; while (k) { Lv * Image[—k]; yy * (float) Lv; zz * yy*yy; if (Lv < Lain) Lmin*Lv; if (Lv > Lmax) Lmax*Lv; a +* yy; b +* zz; с +* yy*zz; d +* zz*zz; У KOSMOM ((a/Nm), (b/Nm), (c/Nm), (d/Nm), Res); Res[0] * (float) Nm; Res[5] * (float) Lmin; Res[6] * (float) Lmax; Res[9] ж (float) (Lmax-Lmin+l); void KIMSTF (Image, Ni,Nj, Nil,Ni2, Njl,Nj2, SHOW Image[]; /¦ Imab« [0...Ni*Nj-l] ¦/ int Ni,Nj, Nil,Ni2, Njl,Nj2; /¦ Число элементов */ float Res [] ; /¦ */ /¦ Статистические характеристики фрагмента изображения */ ft */ { SHOW Lmin,Lmax,Lv; unsigned int k, Mm; float a,b,c,d,yy,zz; int i,j; KIMERS(nKIMSTF:",Mi,Mj, *Nil,ftNi2f kNjl,ftMj2); /¦ Контроль ¦/ a«b«c«d« 0.0; Lmin « Lmax * Image [KARK.(Iil,Ijl)]; for (i*Mil; i<*Mi2; for (k»MARK.(i,Njl), j*Mjl; { Lv * Image[k]; yy * (float) Lv; zz * yy*yy; if (Lv < Lmin) Lmin*Lv; if (Lv > Lmax) Lmax*Lv; * +ж УУ» b +ж zz; с +* yy*zz; d +* zz*zz; У Na « (Hi2-Nil+l)*(Nj2-Hjl+l); KOSMOM ((a/Mm), (b/Nm), (c/Nm), (d/Nm), Res); Res[0] * (float) Na; Res [5] * (float) Las in; Res[6] « (float) Lmax; Res [9] * (float) (Lmax-Lmin+1); Подпрограмма KOSMOM (листинг 3.2) рассчитывает среднее, среднеквадратическое отклонение, коэффициенты асимметрии и
104 Глава 3. Элементарный анализ изображений эксцесса. Ее входными данными служат первые четыре началь- начальных момента. Расчеты проводятся по формулам C.5-3.7). Резуль- Результаты записываются в массив Res. KOSMOM используется также и другими подпрограммами расчета статистических характеристик. Листинг 3.2. Функция KOSMOM •include <etdio.h> /¦ ФАЙЛ — KOSHOH.C */ ¦include <nath.h> «include "KOST.H" void KOSHOMCfloat ¦!, float и2, float иЗ, float ш4, float Re»[10]) /* Пересчет н&лалышх моментов Начальные моменты РЕЗУЛЬТАТЫ ¦1 * СУММА Р(х) ¦ х Re» [13 - среднее ¦2 » СУММА Р(х) ¦ х**2 Res[2] - сигма юЗ * СУММА Р(х) ¦ х«*3 Res[3] - асимметрия ¦4 * СУММА Р(х) * х**4 Re»[4] - эксцесс Если сигма равна нули, то асимметрия и эксцесс также полагается вулевынп */ { int j; float mas « ml^al, /¦ квадрат среднего */ u2 * а2-юаав, /* дисперсия ¦/ u3,u4; for (j*0; j<10; j++) Res[j]*0,0; * ml; if ( «2 > 0.0 ) { u3 * аЗ - Res[?3 » (float) r»qrt (u2); Res[3j « u3/(u2*Res[23); R®s[43 * u4/(u2*u2) - 3,0; Для вывода результатов статистического анализа служат пег. программы KOSPTF и KOSPUF (листинг 3.3) Первая печатае основные результаты в одну строку и предназначена для органи зации табличного вывода результатов многих опытов (измерений) каждый из которых представлен одной строкой. Пример вывода
3. 3. 1. 18 14 62 11 11 11 3.3. Подпрограммы статистического анализа изображений 105 # Сред- Сиг- Асии- Экс- Мини- Макси- Энтро- Раз- нее ма метрия цесс нум пут пия мах 1 3.14 2.73 0.68 -0.42 0.00 10.00 2 4.58 2.24 0.34 -0.41 0.00 10.00 3 0.97 1.79 3.94 17.14 0.00 10.00 Рис. 3.1* Пример вывода статистических характеристик подпро- подпрограммой KOSPTF. показан на рис. 3.1. Вторая печатает в столбец, что удобнее при выводе результатов единственного измерения. Кроме того, она по- позволяет выводить дополнительную информацию: объем анализи- анализируемой выборки, избыточность и максимальную энтропию. При- Примеры вывода приведены на рис. 3.2-3. Обе подпрограммы выводят результаты в указанный файл; ука- указатель на файл вывода является их первым параметром. Второй параметр —- ширина левого поля в знакоместах. Третий параметр в подпрограмме KOSPTF ~ line — означает номер текущей строки таблицы. Этот номер печатается в левой колонке таблицы. Если параметр line равен единице, то перед выдачей строки таблицы пе- печатается заголовок (см. рис. 3*1). Отметим, что подпрограмма не меняет значение line, так что управление нумерацией строк воз- возложено на вызывающую программу. В KOSFUF третий параметр обозначает ширину поля вывода и позволяет выводить результаты примерно по центру поля. Последний, четвертый параметр обеих подпрограмм — массив Res, содержащий результаты статистиче- статистического анализа. Отметим, что обе подпрограммы печатают энтропию только в том случае, если она была вычислена; признаком этого является положительное значение элемента Res[7]. Листинг 3.3. Функции KOSPTF, KOSPUF «include <»tdio.h> /¦ ФАЙЛ — KOSPTF.С ¦/ find ode 5tKOST.H" static ch&r *fruit[10] ¦ { "эбъеи яшборкя", "среднее", "ежгиа"» "гекмметряя", "эксцесс", "минимум", "мысешнум"» "энтропия", " };
106 Глава 3. Элементарный анализ изображений void KOSPTF (Fout, left, line, Res) FILE *Fout; /¦ Файл вывода ¦/ int left; /* Левое поле */ int line; float Res [10]; /* ф/ /* Вывод статистических характеристик в строку */ /* line - номер текущей строки таблицы */ /* (если line*l, то сначала печатается заголовок) */ /ф */ { int j, J « (Res[7]>0) ? 7 : б; if (line««l) { fprintf (Fout,"\n X*s ", left+4, "# "); for (j*l; j<*J; j++) fprintf (Fout, " X7s", fruitCj]); fprintf (Fout, " X7s", fruit[9]); fprintf (Fout, "\n"); } fprintf (Fout,"X*d", left+4, line); for (j«l; j<«J; j++) fprintf (Fout, " *8.2f", Res[j]); fprintf (Fout, " X5.0f", Res[9]); fprintf (Fout, V); } void KOSPUF (Fout, left, width, Res) FILE *Fout; /* Файл вывода */ int left; /* Левое поле */ int width; /* Рабочее поле */ float Res[10]; /ф */ /* Вывод статистических характеристик в столбец */ /¦ ¦/ { int j, zon « left+width/2+2, J « (Res[7]>0) ? 8 : 6; fprintf (Fout,"\nX*s\n",zon+?3,M** Числовые характеристики **"); for (j«0; j<«J; j++) fprintf (Fout, "X*s X9.3f\n", zon, fruit[j],Res[j]); fprintf (Fout, MX*s X9.3f\n", zon, fruit[9],Res[9]); } 3.4. Получение общих и локальных гистограмм Подпрограмма KIMHIS предназначена для формирования общих, a KIMHFR — локальных гистограмм изображений. Общей мы на- называем гистограмму всего изображения, локальной — гистограм- гистограмму его части — заданного прямоугольного фрагмента. Обе про- программы приведены на листинге 3.4.
3.4. Получение общих и локальных гистограмм 107 Гистограмма формируется в массиве Hist, который должен содержать столько элементов (типа int), сколько уровней име- имеет изображение. Элементы массива Hist пронумерованы от 0 до MURMAX, где MURMAX — константа, заданная в головном фай- файле IMAGE.H. Напомним, что эта константа определяет максималь- максимально допустимый уровень элементов изображения. Элементы массива Hist отвечают столбцам гистограммы; ка- каждый из них представляет количество пикселей соответствующего уровня. Если требуется, чтобы столбцы гистограммы отражали не абсолютное, а относительное количество пикселей данного уровня (частоту), нужна соответствующая нормировка, которую удобно делать при выводе гистограммы на печать. Алгоритм формирования гистограммы несложен. Сначала мас- массив Hist обнуляется. Затем в цикле перебираются пиксели исход- исходного изображения. Определяется уровень, записанный в текущем пикселе. Значение элемента массива Hist, номер которого равен уровню текущего пикселя, увеличивается на единицу. Подпро- Подпрограммы KIMHIS и KIMHFR различаются организацией цикла. В первой обрабатываются пиксели всего изображения, для чего до- достаточно одного цикла, во второй — используются вложенные ци- циклы для сканирования фрагмента. Для страховки от выхода за границы массива Hist проверя- проверяется, принадлежит ли уровень текущего пикселя допустимому диапазону уровней 0 ... MURMAX. Если элементы изображе- изображения имеют тип unsigned char, а максимально допустимый уровень MURMAX = 255, то, конечно, выход за границы диапазона невоз- невозможен. Однако при обработке изображений ограниченного диа- диапазона уровней может быть установлено меньшее значение кон- константы MURMAX. Именно в этом случае не исключен выход зь границы массива. Описание массива Hist имеет вид int Hist С MURMAX+1 ]; Это означает, что его элементы пронумерованы от 0 до MURMAX. Что произойдет, если встретится пиксель, выпадающий из ука- указанного диапазона, например такой, значение которого превышает MURMAX? Попытка отразить его в гистограмме как раз и приве- приведет к выходу за верхнюю границу массива Hist. Такие ошибки осо- особенно коварны, они нередко остаются незамеченными и могут вы- вызывать непредсказуемые эффекты, зависящие от обрабатываемых данных. Случаи выхода за границы диапазона свидетельствуют о неблагополучии в процедурах формирования или обработки изо- изображений и должны тщательно изучаться. Поэтому выпадающие
108 Глава 3. Элементарный анализ изображений пиксели следует не обрабатывать, а просто подсчитывать. Если такие случаи были отмечены, то перед выходом из подпрограммы выдается предупреждение с указанием количества выпадающих из диапазона пикселей. В некоторых приложениях, сопряженных с использованием ги- стограммных признаков для классификации объектов, от подпро- подпрограмм получения гистограмм требуется повышенное быстродей- быстродействие. В этой связи описанный выше локальный контроль может оказаться обременительным. Его можно убрать, заменив тело ци- цикла в KIMHFR одним оператором: Hist [ Image [k] ] ++ ; При этом следует также исключить описание переменной knon и вывод сообщения (см. тексты подпрограмм KIMHIS и KIMHFR). Поскольку полный отказ от контроля чреват появлением труд- трудноуловимых ошибок, вместо локального контроля следует исполь- использовать глобальный, при котором сначала все поле проверяется на допустимость уровней и лишь потом вызываются подпрограммы построения гистограмм. Экономия вычислительных затрат дости- достигается за счет того, что при глобальном контроле каждый пиксель проверяется только один раз вне зависимости от последующей об- обработки, а при локальном, если гистограммы вычисляются внутри перемещающегося окна, — много раз. Упомянем о том, что, если гистограммы вычисляются внутри скользящего окна, может оказаться полезным алгоритм рекур- рекуррентного вычисления локальных гистограмм [27]. При этом ги- гистограмма данного фрагмента получается из гистограммы преды- предыдущего фрагмента вычитанием гистограммы «ушедших» и при- прибавлением гистограммы «вошедших» в окно пикселей. Жела- Желающие могут написать в качестве упражнения соответствующую программу. Для получения локальных гистограмм «ушедших» и «вошедших» пикселей можно использовать KIMHFR, но это, ко- конечно, не лучший вариант. При построчном перемещении окна в начале каждой строки требуется заново вычислять локальную ги- гистограмму. Количество начальных фрагментов может быть умень- уменьшено до одного при зигзагообразном перемещении окна, но такая организация сканирования требует кропотливого программирова- программирования. Того же можно достичь и при построчном перемещении, если хранить в памяти гистограмму не только текущего, но и на- начального фрагмента. Тогда гистограмму очередного начального фрагмента можно рекуррентно получать из гистограммы преды- предыдущего.
3.4. Получение общих и локальных гистограмм 109 Листинг 3.4. Функции KIMHIS, KIMHFR finclude <etdio.h> finclude "IMAGE.H" /* ФАЙЛ — KIMHIS.С */ roid KIMHIS Qatge,fi,Wj, Hist) SHOW IeageH; /* Image [0...Ii*Ij-l] */ int Ii,Ij; int Hist [MURMAX+1]; /¦ Уровни 0...MURMAX ¦/ /¦ ¦/ /* Накопление гистограммы изображения */ /¦ - ¦/ { unsigned int k*Ni*Hj; int lv; int knon*0; /* счетчик выпадавших точек */ KIMERR ("KIMHIS", Mi,Mj); /¦ Контроль ¦/ for (lv«MURMAX; lv>*0; lv--) Hist[lv]«0; /¦ Очистка ¦/ vhile (k> { lv * I*age[--kJ ; if ( lv>*0 kk lv<*MURMAX ) Hiet[lv]++; else knon++; } if (knon) printf ("KIMHIS: в гистограмму не вошло %d точекДп", knon); Ўoid KIMHFR (Ieage,Ii,Njf NilfNi2f Mjl,Ij2> Hiet) SHOW Imaged; /¦ Iaage [0.. .Ii*Ij-l] ¦/ int Hi,Hj, Hil,Hi2, Hjl,Hj2; int Hist [MURMAX+1]; /¦ Уровни 0...MURMAX ¦/ /¦ ¦/ /* Накопление гистограммы фрагмента изображения */ /* — - ¦/ { unsigned int k; int i,j,lv; int knon*0; /¦ счетчик выпадавших точек */ KIMERS O'KIMHFR", Hi,Hj, ftNil,ftMi2, ftNjlfftIj2); /¦ Контроль ¦/ for (lv*MURMAX; lv>*0; lv—) Hist[lv3«0; /¦ Очистка ¦/ for (i«Iil; i<»Mi2; for (k*MARK.(i,Ijl), j lv * IiageCk]; if ( lv>*0 kk lv<*MURMAX ) Hist[lv]++; else knon++; } if (knon) printf ("KIMHFR: в гистограмму не вошло %d точек\п", knon);
110 Глава 3. Элементарный анализ изображений 3.5. Гистограммы по маске Подпрограмма КШНМА (листинг 3.5), как и KIMHFR, позволя- позволяет строить гистограмму заданного фрагмента изображения, но в отличие от нее формирует еще одну гистограмму — по заданной маске. Эту вторую гистограмму мы будем называть масочной. По- Поэтому среди входных параметров КШНМА присутствует массив Mask, размеры и положение которого определяют обрабатываемый фрагмент, а содержимое — маску. Таким образом, маска — это не- некоторое изображение, предписывающее, какие пиксели фрагмента учитывать при формировании масочной гистограммы, а какие — нет. Подпрограмма КШНМА устроена таким образом, что учиты- учитываются пиксели фрагмента, соответствующие ненулевым элемен- элементам маски. Подробнее о масках мы расскажем в гл. 5 при рассмотрении локальной фильтрации изображений. Там же будут приведены программы формирования различных масок. Здесь же только от- отметим, что маска являясь в принципе таким же изображением, как и все прочие, имеет одну особенность, которую нельзя игно- игнорировать при программировании. Маска в отличие от изображе- изображения может содержать отрицательные элементы; подобные приме- примеры приводятся в гл. 5. Поэтому для хранения масок используются массивы типа char (напомним, что изображения хранятся в масси- массивах типа unsigned char — синоним SHOW). Во избежание путани- путаницы мы сразу будем придерживаться этого соглашения, несмотря на то что в данном случае маски интерпретируются бинарным образом (т. е. нуль — не нуль). Если бы не это маленькое отличие, для формирования масок можно было бы использовать имеющиеся подпрограммы создания тестовых полей. Но, увы, приходится писать другие. В тесто- тестовом примере мы будем задавать маску подпрограммой KIMFSM. Получается черный прямоугольник, окруженный пустой каймой постоянной ширины. Такая маска может быть использована для выделения объектов на изображении. Более сложные программы формирования масок даются в гл. 5. Возвращаясь к процедуре формирования масочной гистограм- гистограммы, задержимся немного на вопросе о синхронизации пикселей изображения и элементов маски. Пусть, как обычно, г — номер строки изображения, j — номер столбца, Nj — число столбцов. Левый верхний элемент фрагмента соответствует точке изображе- изображения (NiljNjl), размеры фрагмента — Mi строк, Mj столбцов. Тогда t-й строке изображения соответствует строка фрагмента с номером i — Nil 4-1, (где, конечно, Nil < г < Nil + Mi)* Столб»
3.5. Гистограммы по маске 111 цу изображения с номером ,; соответствует столбец фрагмента с номером ,; — Njl + 1 (при аналогичном ограничении на величи- величину .;). Помня, что маска, как всякое изображение, хранится в одномерном массиве, с помощью адресного соотношения A.1) на- находим номер ячейки маски, отвечающей элементу (i,,;) основного изображения; этот номер равен (i-Nil)x Mj + (j-Njl). Это выражение используется в подпрограмме KIMHMA в пре- преобразованном виде: во внешнем цикле (по i) вычисляется компо- компонента, не зависящая от ,;, а во внутреннем — к полученной вели- величине прибавляется текущее значение ;. Листинг 3.5. Функция КШНМА finelude <stdio.h> •include «IMAGE.Я" /¦ ФАЙЛ — KIMHMA.С ¦/ void KIMHMA (I*age,Ni,Nj, Nil,Njl, Mask,Mi,Mj, Hist,Histl) SHOW I*age[]; /* Image [0...Ni*Nj-l] charMask[]; int Ni.Nj, Nil,Njl, Mi.Mj; int Hist [MURMAX+1]; int Histl[MURMAX+1]; g j /¦ Mask [0.. .Mi*Mj-l] /¦ Уровни 0...MURMAX /* Уровни 0...MURMAX /* Гистограмма фрагмента изображения /* и гистограмма фрагмента изображения по маске { unsigned int k; int i.j.lv, и; int ¦/ ¦/ ¦/ ¦/ ¦/ KIMERS ("KIMHMA", Ni,Nj, feNil,feNi2, Контроль ¦/ for (lv«MURMAX;lv>«O;lv—) Hist[lv]«Histl[lv]«0; /¦ Очистка */ for for (k«MARK.(i,Njl), j-Ijl; j<«Nj2; j++, k++) lv * IeageCk]; if ( MaskCm+j] ) Hist [lv]++; Histl[lv]++; >
112 Глава 3, Элементарный анализ изображений 3.6. Анализ гистограмм Статистические характеристики изображений, как уже отмеча- отмечалось, могут быть вычислены двумя способами — непосредственно по изображению или по его гистограмме. Оба способа используют описанный в разд. 3.2 метод, при котором сначала вычисляются оценки начальных моментов, а затем они пересчитываются в цен- центральные. Если ширина столбцов гистограммы равна единице, т. е. со- совпадает с шагом дискретности цифрового поля по уровню, то оба способа дают одинаковые результаты. Мы используем гистограм- гистограммы только такого вида, хотя подпрограммы статистического ана- анализа рассчитаны на общий случай (гистограммы с произвольной шириной столбцов). Подпрограмма KHISAN (листинг 3.6) позволяет вычислять ста- статистические характеристики по гистограмме. Подпрограмма воз- возвращает единицу ори нормальном завершении обработки и нуль, если задана пустая гистограмма. Гистограмма содержится в массиве Hist, элементы которого пронумерованы от 0 до Lh, В нашей интерпретации эти номера обозначают уровни исходного изображения, точнее, равны им. Параметр х10 задает уровень, соответствующий нулевому эле- элементу гистограммы, параметр xkf определяет ширину столбца ги- гистограммы. Эти два параметра введены для общности. При обра- обработке гистограмм цифровых полей они устанавливаются равными соответственно нулю и единице. Последний параметр — массив Res, состоящий из 10 элементов типа float. В этот массив помещаются полученные подпрограм- подпрограммой числовые характеристики. Назначение элементов массит a Res описано в разд. 3.3. Работа подпрограммы KHISAN начинается с обнуления мас- массива результатов Res. Затем уточняются границы непустой зо- зоны гистограммы, а заодно проверяется, содержит ли гистограмма данные. Если все элементы массива — нулевые, то данных нет. В этом случае обработка прекращается, подпрограмма возвращает нуль. Если гистограмма содержит данные, то они обрабатываются. Начальные моменты и энтропия рассчитываются в соответствии с выражениями C.4) и C.11). Суммирование происходит в ци- цикле по элементам гистограммы, от минимального уровня до мак- максимального. Пустые столбцы гистограммы пропускаются. По» еле вычисления начальных моментов вызывается подпрограмма KOSMOM, которая рассчитывает центральные моменты, коэффи-
3.6. Анализ гистограмм 113 циенты асимметрии и эксцесса. Избыточность определяется по формулам C.9) и C.10). Результаты помещаются в массив Res. Листинг 3.6. Функция KHISAN finclude <«tdio,h> /¦ ФАЙЛ — KHISAI.C */ finclude <eath.h> finclude "EOST.I" int KHISAI (RiBt.lh, x!0,xkf, Ren) int HietD, Lh, *10,xkf; float Res [10]; /¦ Статистические характеристики - no гистограмме Hist - nr стограмма, уровни 0 ... La Lh - нажсямальяо доп. уровень в гистограмме ilO - нявви. уроде», соотдетствувяжй Hiet[0] ikf - ««во уровкбА, слжгкх s одном столбце гжстогр&нин РЕЗУЛЬТАТ» R«e[0] - объем выборки Res[6] * мяиияум &•*[!] - среднее Ке»[б] - мажсжяуп Re«[2] - СИГМ& Ree[7] - энтропия (вря xkf*l) Еев[3] * аеаямегри* Ree[83 - избиточвость [1 Ее* [9] - Есд» лисаерсшБ passe* яуя», то асжн«етршя ж эксд«сс т&кхв оояагшвтес жулвв*ши •/ 6ч*»»Я1чШ»«»'вГ ^ Н'Л1Г«1Я»«»«|Я««1 JV«ni !¦ МП «*«К4|» ОТ ЯП И Л1ПГ ПИ" « М М МЯЛ «•¦• *к<|*я' ¦¦ *Г"«1 JM ****<*W ^# float Ьаде,1|,к19я2#вЗэ«4,яю9 «strop, ж9хжу 1в*ж; iitt 1; /¦ граяхш эадоянфияого уч*стл*. list ¦/ for O«0; 1<8j 1*4) !#•[!] » 0.0; Lus * 0; L**x « Lh; fox ( ; Uin<*La*i; Uiis^> if ( Ei.t[L*m] > С ) break; for С ; Uia<»Uax; U%x-) if ( KistCU&x] > 0 > break; if (Laib > SjmubI r^tare 0; /¦ гистогрлнва пуста* */ base » xl(H0.5*(xkf-l); /¦ ср. уроьежь нулевого столица ¦/ ¦1 » в2 ж вЗ • *4 * ввя * entrop » 0.0;
114 Глава 3. Элементарный анализ изображений for (l-Lmin; 1<*Lb&x; { if h entrop SUB Bl b2 b3 b4 <!Hist[l]> « Hist[1]; ¦¦ h*log(h); ¦- h; ¦» h*x; ¦¦ h*xx; +¦ h*xx*x; ¦« h*xx*xx; continue; x ¦ b&se + xkf*l; xx ¦ x*x; KOSMOM ((ml/sum), <b2/sub), (вЗ/sub), <b4/sub), Res); Res[0] Res[5] Res [6] Res[73 Res[9] Ib&x Res[8] return sub; xlO ¦ xkf*Uin ¦ xkf/2; xlO ¦ xkf*La*x ¦ xkf/2; ( log(suB) - entrop/suB ) / logB.0) ; xkf*(LM«-L«in+l); log(Re8[9]) / logB.0); /¦ н&хсжм&льа&я эмтропяя */ 1.0 - (Qb&x>0) ? Res[7]/lB&x : 0.0); 3.7. Вывод гистограмм Подпрограмма KHISTA предназначена для вывода гистограмм в символьном виде. Ось абсцисс расположена вертикально, ось ор- ординат — горизонтально, так что на рис. 3.2, как и на экране дис- дисплея, столбцы гистограммы превращаются в строки. По оси аб- абсцисс отложены уровни исходного изображения, по оси ординат — количество пикселей, имеющих данный уровень. Масштаб по оси ординат выбирается автоматически, из условия возможно более полного использования ширины поля вывода. Текст подпрограм- подпрограммы приведен на листинге 3.7. KHISTA может выводить гистограммы в любой указанный файл. Первым параметром подпрограммы является указатель на файл вывода. Для вывода на экран следует использовать обраще- обращение KHISTA (stdout, ... ); Для вывода на принтер следует открыть файл с именем PRN. Ги- Гистограммы, отпечатанные на бумаге, будут выглядеть лучше, если использовать узкий шрифт при увеличенном поле вывода и умень- уменьшенный межстрочный интервал. Увеличивать поле вывода и ле- левое поле надо потому, что эти параметры задаются в знакоместах, а при узком шрифте количество знакомест в строке возрастает.
3.7. Вывод гистограмм 115 Управлять шрифтом и межстрочным интервалом лучше всего про- программно. Мы не приводим здесь такие программы, так как упра- управляющие коды зависят от марок используемых принтеров. Гистограмма передается в подпрограмму в массиве Hist, кото- который является ее вторым параметром. Следующий параметрх Lmax, задает максимальное значение абсциссы гистограммы. Количе- Количество элементов в массиве Hist должно быть не меньше Lmax-fl. Параметр Lmin определяет, с какой абсциссы начинать вывод ги- гистограммы. Параметры хЮ и xkf имеют тот же смысл, что и в подпрограмме KHISAN. Параметры width и leftz задают общую ширину поля вывода (число знакомест) и левое поле, которое входит в общую шири- ширину. Внутри подпрограммы смысл параметра width меняется: после оператора width - = leftz; он обозначает ширину зоны вывода гистограммы без левого поля. В зависимости от соотношения между наибольшей высотой столб- столбца и шириной зоны вывода выбирается масштаб по оси ординат. Подпрограмма возвращает единицу при нормальном заверше- завершении и нуль, если исходный массив не содержит данных. Бели требуется не абсолютная, а относительная оцифровка Оси ординат, то вместо KHISTA следует вызывать подпрограм- подпрограмму KHISTO (листинг 3.7), имеющую тот же набор параметров. Отличие состоит в том, что по оси ординат откладываются ча- частоты появления пикселей данного уровня, а не их количество (рис. 3.3). Подпрограмма KHISTW (листинг 3.7) предназначена для ин- интерактивного управления выводом гистограмм и числовых харак- характеристик. Первым ее параметром является массив Hist, содержа- содержащий гистограмму. Затем следует Lh — число элементов указан- указанного массива без единицы, т. е. максимальный уровень исходного Поля. Следующие два параметра, х10 и xkf, имеют тот же смысл, Что и в KHISTA. Последний параметр, statistic, управляет выда- выдачей числовых характеристик. Бели он не равен нулю, то по гисто- гистограмме рассчитываются и выводятся вместе с ней числовые ха- характеристики. Если этот параметр равен нулю, выводится только гистограмма. В режиме диалога запрашивается и устанавливается общая ши- ширина поля вывода, ширина левого поля, имя файла вывода и спо- способ обработки пустых краев. Последнее требует пояснения. По- Поскольку изображения могут иметь диапазон уровней от 0 до 255,
116 Глава 3. Элементарный анализ изображений О 10 20 30 40 50 60 70 0 !***************************************** 1 t************************** 2 !************************** 3 !************************ 4 !***************** $ I***************** $ ;************** 7 !************ 8 !*****! « 9 !*****! ! 10 !¦¦¦¦¦! ! 11 ! ! ! ** Числовые характеристики *¦ объем выборки среднее сигма асимметрия эксцесс минимум максимум энтропия избыточность размах 320.000 3.137 2.728 0.683 -0.425 0.000 10.000 3.181 0.080 11.000 Рис* 3,2* Пример вывода гистограммы и статистических харак- характеристик подпрограммами KHISTW и KHISTA. Результаты анализа поля, показанного на рис. 1.3. под гистограмму обычно отводится массив, содержащий 256 яче- ячеек. Вместе с тем конкретное изображение может иметь значи- значительно меньший, ограниченный диапазон уровней. Гистограмма полного диапазона выглядит при этом HeKpacneot так как имеет длинные пустые «хвосты». Поэтому предусмотрена возможность подавления их вывода. Подпрограмма KHISTW вызывает для вывода гистограммы подпрограмму KHISTA или KHISTO, для расчета и печати чи- числовых характеристик — описанные ранее KHISAN и KOSPUF. KHISTW обеспечивает возможность повторного вывода гисто- гистограммы и результатов анализа. При этом можно изменить уста- установленные рацее параметры печати и файл вывода.
3.7. Вывод гистограмм 117 О О,OS 0.10 0.15 0.20 О !******************************************* \ 1 **************************** 2 {**************************** 3 ;************************* 4 ******************* i 5 I****************** 1 $ t*************** 7 !************* 8 ;***** ! 9 ****** ! 10 !****¦ ! 11 ! ! i * i i i • i • i 1 * f • 1 • 1 1 • 1 * 1 * 1 • 1 • 1 • 1 1 • 8 1 1 1 1 1 1 • • 1 • 1 • Рис. 3 3. Пример гистограммы с относительной оцифровкой оси ординат. Листинг 3.7. Функции KHISTA, KHISTO, KHISTW finclude <stdio.h> •include "IMAGE.fi" •include "KOST.H" •include "KDV.H" /¦ ФАЙЛ — KHISTA.С ¦/ •define TRUE 1 •define FALSE 0 •define error(ett) { printf(s,t>; STOP. > •define b'lDMAX 2S6 /* яределья&я «крина поля ¦/ int IIISTA (tout, Hist, FILE *fout; int Hi*t?j; int L»in, xl0,ikf, ?idth,ieftz) ,?idth»leftz; / /* ПОСТРОЕНИЕ ГНеТОГРАЮШ В ПОЛЕ ЗАДАННОЙ ШЖРЩМ с &бсояжтзой оцифровкой я авгомасаггабнроваЕкея ко оси ординат Ось абсцасс — вертикальна! fout - указатель на ф&йя вывода Hist - гкстограмил, уровня [0 ... LmaJt] La&x * накснмальний уровень гистограммы Lain - печатать гмстограниу с этого уровня (интервала) хЮ - фяэнческая абсцисса середины 0-го интервала
118 Глава 3. Элементарный анализ изображений xkf - ширина интервала. width - внрвва поля вывода, знакомест (вклачал левое поле) leftz - левое поле Еслв массив Hist пуст, то п/п вместо гистограммы выводит предупреждавшее сообщение и возвращает О, иначе выводит гистограмму и возвращает 1. */ /* — ¦/ { float ahst; register int j; int Нвах*0; /* это будет значение максимума */ int 1ов, Jb; int Iscale,JCD,Jwid,1,MCD,*D; static int JCDO[1O]«{1O, 8, 6,10, 8, 8, 6,10, 8, 6}; /* мест/дел.*/ static int MCD0[10]«{10,10,10,20,20,2S,25,50»S0,50}; /* цена дел.*/ width -¦ leftz; if (width<7 II width>WIDMAX) error ("KHISTA? width«Xd\n",width); if ( Laax<l || LaaxMOOO ) error ("KHISTA? Laax«Xd\n", Laax); if ( Lain<0 ) error O'KHISTA? Lain«Xd\n", Lain); if < Lain>LBax) Lain"Laax; for (j"Lain; j<«Laax; j*+) if (Hist[j]>Haax) Haax ¦ Hist[j] ; if ( Haax<«0 ) { fprintf(fout,"\tHaccHB пуст!!!\пи); return 0; } loa * FALSE; /* масштабирование ве требуется */ Ja ¦ 1; /* декадный множитель - единица */ Iscale ; /* шкала - нулевая */ ahst * 1.0; if (Haax>width) { loa ¦ TRUE; /* проводим масштабирование */ for ( ;Ja>0 ; Ja*«10) { for (Iscale*0; Iecale<«9; Iscale++) { ahst « (float) JCDO[Iscale]/(MCDO[Iscale]*Ja); if (ahet*Haax<«width) goto lb2S0; } lb250: MCD « HCDO[Iscale]*Ja; /* цена деления */ JCD « JCDO[Iscale]; /* знакомест/деление ¦/ Jwid » (int)(Haax*ahst)*S; /¦ фактнч. ширина поля */ if (Jvid>WIDHAX) Jwid « WIDMAX; ID * Jwid/JCD; /* число делений ¦/ fprintf (fout,"Xed11, leftz+7,0); for (j-1; j<-ID; j++) fprintf <fout,"X*d", JCD,j*MCD); fprintf (fout,"\nM); /¦ собственно построение гистограммы ¦/
3.7. Вывод гистограмм 119 Jwid « (Jwid/JCD)*JCD; /* число обрабатываемых знакомест */ for (l«LBin; 1<«1.вах; 1-м-) { Ja ¦ Hist[l]; if Aов) Jb * (int)(Je*ahst+O.S); /* число астерисков */ /* формируем строку в процессе вывода. */ fprintf (fout,"%*d Iй, leftz+5,xlO-H*xkf); for (j«l; j<*Ja; j-мО fprintf (fout,"Xc", >¦>); for ( ; j<«J*id; j++) fprintf (fout,"%c", (j%JCD) ?'»:'!'); fprintf (fout,"\n"); } return 1; int KHISTO (fout, Hist,Laax,Lain, xlO,xkf, width,leftz) FILE *fout; int Hist [] ; int Laax,LBin, xlO,xkf,width,leftz; /¦ ПОСТРОЕНИЕ ГИСТОГРАММЫ В ПОЛЕ ЗАДАННОЙ ШИРИНЫ с относительной оцифровкой и автомасвтабированием по оси ординат. Ось абсцисс — вертикальна.! fout * указатель на файл вывода. Hist - гистограмма, уровни [0 ... Lb*jc] L«ax - максимальный уровень гистограммы Lain * печатать гистограмму с этого уровня (интервала.) х10 * физическая абсцисса середины 0-го интервала xkf - нирнна интервала width - ширина поля вывода,, энахонест (включая левое поле) leftz * левое поле Если массив Hist пуст, то п/п вместо гистограммы выводит предупреждавшее сообщение и возвращает О, иначе выводит гистограмму и возвращает 1. */ Ж ^В ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ 9Ш> flR ЩШ ЩР ЩШЪ ШШ> 1ШЬЩШЪ ЩШЪШ^ ЩШЪ 1ШЬ 1ШЬ 1ШЬ 1ШЬ 1ШЬ 4^ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ШШ ШШ ЩШЪЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ 1ШЬ ЩШЪ СВ ЩШЪ ЩЛ ЩШ ЩШЪ ЩШЪ ЩЛ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШЪ ЩШ> ЩШЪ ШЙШ ЩШЪ 1W ЩШЪ |^Е щ { float ahst, ahtu, ecd, Хв; register int j; int Ннах*0; /* это будет значение максимума */ int sim»0, Je, kdlg; int Iscale,JCD,Jwid,l,ID; •tatic int JCD0[10]»{10, 8, 6,10, 8, 8, 6,10, 8, 6}; /¦ мест/дел.*/ static int MCDO[10]»{10,10,10,20,20,25,25,50,50,50}; /¦ цена дел.*/ width -¦ leftz; if (width<7 || width>WIDMAX) error ("KHISTO? ?idth»Xd\n",width); if ( L»ax<l || Laax>1000 ) error ("KHISTO? Uax>Xd\n", L-ax); if ( Lain<0 ) error ("KHISTO? Lain-%d\n", Lain); if ( Lain>Laax) Lain^Laax;
120 Глава 3. Элементарный анализ изображений for (j«Lein; j<«L«ax; < J« - Hiet[j], «ив ¦« Jb; if ОвЖвах) Ha&x » Jb; > Хш * (float)Нв&х / sub; if ( Нвах<»0 ) { fprintfCfmit/'UHacc*» дуст! !!\пи); return 0; > ah«t » 1.0; kdlg ¦ 1; /¦ подбор диапазон* - грубо */ while (ahst*XB*1.2 < width ) { *het*«iO; kdlg—; } /¦ подбор диапазона. - точно */ for (Iecale»0; Isc&leOS; Iecale**) { ahtu ¦ ahet ¦ JCDOClec&l®] / ИС0О[1вса1в]; if ( &htu*XB <* width ) goto lb2S0; } ahst/*10; kdlg**; Xscale^O; &htu*ahet; lb250: acd * MCDO[Iscale]/ahet; /¦ цена деяевил */ ahet * ahtu/eua; JCD * JCD0[Iscale], /¦ знакомест/деление */ Jwid * (int)(HB&j:**hst)+5; /¦ ф&хтмч. мкркн«. поля */ if (Jwid>VIDH*X) Jwid > WIDHAX; ID * Jwid/JCD; /¦ число делений */ Jwid ¦ ID * JCD; /¦ число обрабатываемых знакомест */ 1 « (JCD<8) ? 2 : 1; /¦ и*.г оцифровки ¦/ if ( 1«»1 t* ( Iecale*»S И l8C4le»6) ) kdlg—; fprintf (foett"X*d"t for (j»l; j<«lD; j*»l) fpnntf (fovt,"X*.*f"9l*JCDt-kdlg9 fprintf (foutf"\nfl); for (l*Lnin; K«L»»jt; 1**) /* ясстроеши» гжстогрилиш ¦/ { Jb * CintXHist[lJ*&!uit*N).5); /¦ чшспо кстржсмш */ /* форнируен строку в процесса вывода ¦/ fpriatf (foiit,"X«d !"t leftz*SfxX0-H*xkf); for (j*l; j<»J«; j-м.) fprintf <f9ut,"Xclfr »*O; for ( ; j<*Jwid; j4*) fprintf <fout/'Xc"f (j%JCD) ? > > - MO; fprintf У return 1;
3.7. Вывод гистограмм 121 Ўoid KHISTV <Hist,Lh,xlO,xkf, statistic) int Hist 0; int Lhtxl0,xkf, statistic; /¦ IFTEPAKTIBROE УПРАВЛЕНИЕ ВЫВОДОМ ПСТОГРДШШ Hist * гистограмма, уровни [0 ... Lh] Lh - максимальный уровень гистограммы хЮ * фазическая абсцисса середины 0-го интервала xkf * вхряна интервала statistic - числовые х~ки вычислять? 1 - да, 0 - нет ¦/ { FILE *fopen(); FILE *fout; float Res[10]; char filnaae[32]; int j; static int width»64; /¦ поле вывода гистограммы, знакомест */ ; /* левое поле, знакомест */ /* границы непустой сердцевины ¦/ /* уровень для ее поиска ¦/ /¦ указатель и«. файя вывода static int int L»in,Laax; int H0*0; int LI,I int U * 0; static char repeat * 'Y'; static char static char Oab * »Y» /* границы зоны вывода гнет. */ /¦ вмвод * на экран */ /* вывод - повторять */ /* пустые края - обрезать */ /¦ оцифровка осей - абсолютная ¦/ fout * etdout; /* Найдем границы непустой области гистограммы */ for ,Aлг»*0; iain<*Lh; lmin*+) if <Hi9t[L«in]>H0) break; for (Uiax«Lh; L»in<»LtKaz; 1лаж-*> if (Hiet[Uex)>H0) break; if (Lain>Laax) { printf('fRlISTV: Гистограмма пуста!W); STOP. } if (Lbxii>0> Lain—"; if (Laax<Ui) Laax**; if (statistic) EEISAI (Hist,Lh.ilO,xkf,Res); loop { puts (m<); KDVIiT ("Общяя вирияа поля вывода width', i?idth,8,220); IDVIIT ("П*мю поле lpl", tipl,0,aiith-8), Li«0; L2«Lh; if (ХВГП?В("Нустме края обрезать?",kbo)> < Ll«Lain; LMaax; > абсоявтная?" ,ftOab>;
122 Глава 3. Элементарный анализ изображений /* ВЫВОД ГИСТОГРАММЫ */ fprintf (fouttllX*elltlpl*lt11 "); for (j«width*7; j>lpl; j—) fprintf (fout,"•">; fprintf <fout,"\n"); if <0ab««"Y> || Oab*«>y>) KHISTA (fout,fiist,L2,Ll, xlO.xkf, width,lpl); else KHISTO (fout,Hiet,L2,U, xlO,xkf, widthДpi); if <!U) PAUSE. if (statistic) KOSPUF (fout,lpl,vidth-lpl,Re8>; fprintf Cfo\itt"%*m"tlpl+lt" "); for <j*widthVT; j>lpl; j—) fprintf (fout,"«">; fprintf (fouVW); if (U) { U»0; fcloee(fout); fout"8tdout; /¦ закрыли файл ¦/ printf (" Гистограмма выведена в Xe\n",filna*e); } LB3: puts С1"); if ( ! KDVYES ("Вывод повторить?", trepeat) > return; KDVIIT ("Куда: 0-экран, 1-файл", kU,0,l); if (U) { printf ("\n Файл вывода > "); gets (filnaae); if (Kfout « fopen (filnaee, "«") )) { printf ("Файл X» ие открывается!\n", filnaae); U » 0; goto LB3; > 3.8. Тестовые программы Демонстрационные программы К1МН1 и KIMH2 (листинги 3.8 и 3.9 соответственно) содержат примеры обращений к описанным выше программам. Программа КШН1 проводит статистический анализ изображения или его фрагмента одним из двух способов (по выбору): либо непосредственно, либо через гистограмму. Про- Программа КШН2 предназначена для проведения статистического анализа по маске с помощью гистограмм. Обе программы позволяют сформировать тестовое изображе- изображение заданного размера. Память под него распределяется динами- динамически; при ее нехватке на экране появляется предупреждающее
3.8. Тестовые программы 123 О 10 20 30 40 0 !**** ! ! ! ! 1 !******** !••! 2 I ******************** ! ! 3 !**************************************** 4 ***************************** 5 !**************************** 6 !************************ 7 I******************** 8 1******** ! ! 9 \******** ! ! 10 ***** ! ! 11 ! ! ! ** Числовые характеристики ** объем выборки 192.000 среднее 4.533 сигма 2.244 асимметрия 0.339 эксцесс -0•414 минимум 0.000 максимум 10.000 энтропия 3.142 избыточность 0,092 размах 11.000 Рис. 3.4. Анализ поля (рис. 1.8) по маске. Маска — черный прямо- прямоугольник размером 12 х 16, ширина каймы — 2. Результаты анализа внутри прямоугольника. сообщение и происходит останов. Тип тестового изображения выбирается в интерактивном режиме с помощью подпрограммы KIMGAU. Программа KIMH1 позволяет выбрать один из четырех видов обработки заданного поля: для всего поля или для фрагмента можно получить статистические характеристики как непосред- непосредственно, так и через гистограмму. При использовании гисто- гистограмм вызывается одна из подпрограмм обработки KIMSTA или KIMSTF, результаты выводятся с помощью KHISTW. Для пря- прямого статистического анализа вызывается KIMSTA или KIMSTF, результаты выводятся на экран, причем дважды: в столбец с по- помощью KOSPUF и в строку с помощью KOSPTF. Программа KIMH2 использует для получения маски подпро-
124 Глава 3. Элементарный анализ изображений 0 1 2 3 4 5 6 7 8 9 10 11 0 i • i i ; i • i • ! 1 • • i • i • i • 10 20 30 40 50 60 *¦¦***¦¦*********************#*$*****# ********************** 1 ************** 1 ¦ i i 1 1 1 1 1 1 I 1 1 1 1 [ 1 1 1 1 > ¦ 1 1 1 1 1 1 1 ; i 1 I 1 1 1 1 I 1 ( J > 1 1 1 1 \ i i ¦ * ** Числовые характеристики объем выборки среднее сигма асимметрия эксцесс минимум максимум энтропия избыточность 128.000 0.969 1.794 3.945 17.141 0.000 10.000 1.624 0.531 размах 11.000 1 * t i • 1 1 * t I I m 1 • 70 i * • I * I • I I 1 I I * t * * I * Рис. 3.5. Анализ поля (рис. 1.8) по маске. Маска — черный прямо- прямоугольник размером 12 х 16, ширина каймы — 2. Результаты анализа каймы. грамму KIMMSM, находящуюся в том же файле (листинг 3.9). Маска имеет вид черного прямоугольника (уровень единица); ок- окруженного светлой каймой (уровень нуль). Ширина каймы со всех сторон одинакова, эта ширина задается в интерактивном режиме. Подпрограмма KIMHMA строит общую гистограмму фрагмен- фрагмента (Hist) и гистограмму по маске (Histl), т. е, для участка фраг- фрагмента, покрываемого черным прямоугольником маски. Гисто- Гистограмма каймы (HistO) получается вычитанием масочной гисто- гистограммы из общей. Такую гистограмму можно назвать «антима- «антимасочной». Затем вычисляются и печатаются в табличном виде ре- результаты статистического анализа всех трех гистограмм: общей. масочной и «антимасочной» (рис. 3.1). Далее, при положительном ответе на запрос с помощью под-
3.8. Тестовые программы 125 программы KHISTW могут быть выведены сами гистограммы, а также подробные результаты их анализа (в столбец). Обратим внимание на то, что KHISTW проводит статистический анализ самостоятельно. На рис. 3,2 показаны результаты статистическо- статистического анализа изображения с рис. 1,8, а на рис. 3.4 и 3.5 — результаты анализа того же поля по маске. Листинг S.8. Демонстрационная программа KIMH1 ¦include <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ -- KIMHI.C ¦/ «include $>KOST.H" •include "KDV.H" /* Анализ изображений */ void eainO < static SHOW *Xiaag®; static int Hist [ HURHAX+ 1 ] ; int int int , , char uear^'i*, End-'IP; put» ("\n\n\t K1HH1 - Ая&яяз изображений"); do { puts (""); if ( ucar»»'i* П «KDVYES ("Картинку сохранить?",tucar) ) { if (ucar lss Ji') free <Ia$*ge>; KBVIKT ("Высота поля, строк *i>\ tii, O,SO0); KDVIIT СШ?шн& mon*f стшЫш fj", «j» 0,500); * aalloc (S if ( fImage ) { put» ("Нет столько нанят»!"); STOP. > KIHGAF (Image, Ii»Ij, klv); } K10TC (Image, Ii,Ij); PAUSE. pats ("Быбержте вид обработка"*); XOVIIT ("О - все коле, 1 - фрагмент поля", fcufra,0,i); KJDYIIT ("О - статкстшса, I ~ гистограмма", fttibi»,0,l); if (trfra) { IDVIIT С1 Фрагмент со строки", ftlil,l,li); EDVUTT (и не строку", fe«i2,Iil,fi>; KD?I»T (м со столбца", fe»jl,l,fj); RDVIIT ("
126 Глава 3. Элементарный анализ изображений printf ("\n Принято задание:"); if (uhis) { puts (и накопление н обработка гистограммы"); if (ufra) KIMHFR (I«age, Ii,Hj, Iil,Ii2, Ijl,lj2, Hist); else XIHHIS (bage, li.Ij, Hist); RHISTW (Hist, U, 0,1, 1); } else { float Res[10]; puts (м пряное получение статистики поля'1); If (ufra) KIMSTF (Image, li.Mj, Iil,Hi2, Hjl,Ij2, Res); else KIHSTA (Ieage, Mi,Ij, Res); pute (M\n Ознакомьтесь с результатами, please:\nH); KOSPTF (stdout, 0, 1, Res); KOSPUF (stdout, 0,64, Res); > } while ( «KDVYES (м Закончить работу?M,*End) ); free (Image); EID. Листинг З.9. Демонстрационная программа KIMH2 н функция KIMMSM •include <stdio.h> finelude MIMAGE.HM •include "XOST.B" •include "KDV.H" void aainO SHOW «Image; char *Hask; static int Hist [ HURMAX+1 ]; static int Hist [ MURMAX+1 ]; static int HisO [ MURNAX^l ]; int I1-20, Ij-32; int Iii-2, Ijl»2, Hi-7, Hj-7; int j, lv»32; char ucar^'i', uhs>!', End*'!' /¦ ФАЙЛ — KIMH2.C */ /* Анализ изображений */ float Res[10]; puts ("\n\n\t KINH2 - Анализ фрагментов изображений по наске"); do { puts (""); if ( ucar-»'i> II !KDVYES ("Картинку сохранить?M,*ucar) ) < if (ucar !* *iO free (Iaage); ucar»'Y';
3.8. Тестовые программы KDVIIT ("Высота поля, строк Mi",*Mi,0,220); KDVIIT ("Ширина поля, столбцов Nj",*Mj,0,220); if ( !( Iaage * (SHOW *) »alloc(Ki*Kj) ) ) { puts ("Нет столько памятн!"); STOP. KIMQAU (Ieage, Mi,Mj, klv); XIOTC (I«age, Mi,Mj); PAUSE. 127 KDVIMT (" Фрагмент -о строки", *Iil,l,Ii); KDVIHT (" строк во ^^гненте11, *Mi, 1, Hi-Nil+1); KDVIMT (" со столбца", fcljl,l,lj); KDVIHT (" столбцов", *Mj, 1, Ij-Ijl+1); if ( !( Mask « (char «0 »alloc(Mi*Mj) ) ) { puts ("Нет памяти для наски!"); STOP. } KIMMSM (Mask, Hi,Hj,l); /¦ Ввод иаскн ¦/ /¦ Накопление и обработка гистограммы */ KIMHMA (Iaage, Hi,Hj, Hil,Hjl, Mask,Hi,Hj, Hist,Hisl); for (j-MURMAX; j>«0; j—) HisOCj] « HistCj] - HislCj]; puts ("\n\Статистика фрагмента: 1-общая, 2-маска>0, 3-маска*0м); KHISAN (Hist,lv,0,1,Res); KOSPTF (stdout, 1,1, Res); KHISAN (Hisl,lv,0,l,Res); KOSPTF (stdout, 1,2, Res); KHISAM (His0,lv,0,l,Res); KOSPTF (stdout, 1,3, Res); if( KDVYES (" Гистограммы выводить?",fcuh) ) { puts("\n Статистика фрагмента: общая1'); KHISTU (Hiet,lv, 0,1, 1); pute("\n Статистика фрагмента: иаска>0"); KHISTU (Hisl,lv, 0,1, 1); put»("\n Статистика фрагмента: иаска*0"); KHISTU (Hie0,lv, 0,1, I); } free (Mask); } while ( !KDVYES (" Закончить работу?",ftEnd) ); free (Iaage); EID. void KIHKSH (Haek,Ni,Hj,Mur) char Mask [] ; int Ni,Nj,Mur; /* Черный прямоугольник /* Уровни: фон * 0, прямоугольник ¦ Миг i int i,j; int Свах * ((Vi<Nj)?Hi:Hj)/2; static int bord*l; */ /* макс, ширина каймы */ /* ширина пустой каймы */
128 Глава 3. Элементарный анализ изображений int кО, к - li*lj; printf("IIMHSM: Черный прямоугольник «4 поле %d ¦ %d\n",Ii,lj); while (k) Muk[-k3 - 0; KDVI1T (жржн& пустой каймы", tbord» l,Gnaz); for (kCHHARKw(bord+l,bora>l), i»Ii-2*bord; for (k«*0t j»Ij-2*bord; j>0; j—, k+O M**k[k] ¦ Hur; Прототипы функций, описанных в данной главе, содержатся в файлах KIMAG.H (см. листинг 1.27) и KOST.H (листинг ЗЛО). В первом из них находятся прототипы функций верхнего уровня, непосредственно обрабатывающих изображения; во втором — про» тотипы вспомогательных подпрограмм статистического анализа и вывода результатов в виде таблиц и гистограмм. Файл KOST.H можно присоединить к файлу KIMAG.H, включив в него директи- директиву препроцессора «include "KOST.H" или попросту объединив файлы. Листинг ЗЛО, Головной файл KOST.H /¦ t&ift — iost.i ¦/ int IBISAI Ciftt EifttD» i»t Lb, iat xO, lat zkf, float iat MIST A (FILE *fout» tut HietD» iftt 1млх9 bit l*in, i»t xO, iat xkt9 int width, iat ittt EBISTO (FILE ¦font,» i&t IxetOs i»t Lauc9 iat L»in, int жО, iot xkt, i»t width, itit l«f>; void IHISTW Ciftt listO, int Lh, i&t xO, iiit xkf, ist void ШЯЖШ (char Шшт%П, imt li, int Ijy int Инг); void lOSffOH (float It float 2, float Э, float 4» float i#*[10}}; Ўold XOSPTF (FILE *Fotft, 1st loft, int lift«, float Ўoid IOSPUF (FILE ¦Fout, int l«ftt int sicith, float i**O);
ГЕОМЕТРИЧЕСКИЕ ПРЕОБРАЗОВАНИЯ Алгоритмы геометрических преобразований плоских изображений практически не освещены в отечественной литературе, несмотря на их важное место в решении многих прикладных задач. Доста- Достаточно назвать такие области применения, как обработка аэрокос- аэрокосмических снимков, построение инвариантных к ракурсу систем распознавания, моделирование формообразования. С геометриче- геометрическими преобразованиями приходится сталкиваться при создании перспективных систем технического зрения, анализа и синтеза изображений трехмерных объектов. Под геометрическим преобразованием изображения мы будем понимать взаимно однозначное точечное отображение плоскости XOY', на которой задано исходное изображение, в плоскость UOV. Среди множества возможных преобразований ограничимся рас- рассмотрением частного, но важного случая — аффинного преобра- преобразования плоскости. 4.1. Аффинное преобразование и его подгруппы Аффинное преобразование, как известно, является самым общим взаимно однозначным отображением плоскости на плоскость, при котором сохраняются прямые линии. Сохраняется также отно- отношение длин отрезков, лежащих на одной прямой (или на парал- параллельных прямых) и отношение площадей фигур. Параллельные прямые переходят в параллельные. Аффинное преобразование аналитически выражается невыро- невырожденным линейным преобразованием и = ах + by ¦+- с, v = dx-fey-f /, D.1)
130 Глава 4. Геометрические преобразования причем а Ь d e Аффинное преобразование образует группу. Это означает, что любая композиция последовательно выполняемых аффинных пре- преобразований (произведение преобразований) также является аф- аффинным преобразованием. Любое аффинное преобразование име- имеет обратное, которое также является аффинным. Произведение прямого и обратного преобразований дает единичное преобразова- преобразование, оставляющее все на своих местах. Параметры обратного преобразования х = Аи + Bv + C, у = Du + Ev + F D.2) можно получить, решив систему уравнений D.1) относительно х, у: А- — В ----- Г ~ bf ~ еС det det det dec det det где det = ae — bd — определитель DЛ). Если A — a) x A — e) ф bd, то аффинное преобразование имеет неподвижную точку (Хн, Ун): е) - D 4^ Свободные парамеа ры аффинного преобразования связаны с координатами неподвижной точки простыми соотношениями с = A - a)A"H ~ / = (l-c)rH-dXH. D.5) Подгруппой аффинной группы преобразований является груп- группа подобия: и = kx cos w j- ky sin t<; -f c, v = —irxsin w -f Art/cost/; -f /. D.6)
4.2. Программы аффинного преобразования 131 В свою очередь в группу преобразований подобия входит под- подгруппа плоских движений, называемая также группой изометри- изометрических преобразований и = х cos w 4- у sin w 4 с, г; = — х sin к; 4 У cos iu 4- /, D-7) г/ = г; =г и группа трансляций, или плоскопараллельмых переносов (сдви- (сдвигов) и = х 4 с, v = У + Подробный анализ возможностей различных групп преобразо- преобразований плоскости дан в прекрасной монографии В. С. Файна [28]. В [29] рассмотрены подгруппы аффинной группы преобразований плоскости, на стр. 88 приводится их достаточно полный перечень и аналитическое описание. Рассматриваются вопросы построе- построения сложных преобразований произведением (последовательным выполнением) простых. Интересующихся специальными вида- видами преобразований, применяемых при обработке аэрокосмических снимков, мы отсылаем к работе [30]. Там же описаны оригиналь- оригинальные методы ускорения процесса вычислений за счет специальной группировки машинных команд, оптимизации выборки из памяти и т. д. Описанное ниже программное обеспечение, восходящее к ра- работам [31, 32], включает в себя программы аффинного преобразо- преобразования общего вида и ряд вспомогательных подпрограмм, позво- позволяющих переводить такие физически прозрачные понятия, как угол поворота, масштаб, координаты неподвижной точки и т. п., в достаточно абстрактные аффинные параметры, которые только и «понимает» подпрограмма преобразования. 4.2. Программы аффинного преобразования Мы рассмотрим два родственных алгоритма аффинного преобра- преобразования, условно именуемых нами преобразованием по ближайше- ближайшему дискрету и преобразованием с разбиением на подклетки. Со- Соответствующие подпрограммы называются KIAFFI и KIAFFS; их
132 Глава 4. Геометрические преобразования тексты вместе с текстом вспомогательной подпрограммы KIAFOB приведены на листинге 4.1. Преобразование по ближайшему дискрету выполняется следу- следующим образом. Для каждой точки (пикселя) нового изображения вычисляется ее прообраз — точка исходного поля. Полученные ко- координаты, округленные до целых чисел, указывают на некоторый пиксель исходного поля. Значение этого пикселя присваивается текущему пикселю нового изображения. Может оказаться, что вычисленный прообраз текущей точки лежит вне границ исходно- исходного изображения. Тогда в текущий пиксель нового поля заносится уровень NODATA. Обратим внимание на то, что при вычислениях фактически ис- используется не то аффинное преобразование, которое требуется вы- выполнить, а обратное ему! Это связано с тем, что сканируется новое изображение и по известным координатам его точек рассчитыва- рассчитываются их старые положения. Внимательный читатель может спро- спросить, почему сделано так, а не наоборот. Не лучше ли сканировать исходное изображение, рассчитывая новые координаты точек по формулам прямого преобразования? Оказывается, нет! Выбран- Выбранный нами алгоритм гарантирует, что ни одна точка нового поля не будет пропущена и что все его пиксели получат определенные значения. При ином порядке на преобразованном изображении появляются пропуски, которых особенно много, если преобразова- преобразование сопровождается растяжением. Проще не допустить возникно- возникновения пропусков, чем впоследствии заниматься их восполнением. Подпрограмма KIAFFI, реализующая описанный выше алго- алгоритм, имеет 7 параметров. Это имена исходного и преобразован- преобразованного полей, их размеры, а также массив, содержащий параметры обратного аффинного преобразования в ячейках с 1 по 6. Подроб- Подробнее назначение ячеек этого массива будет рассмотрено ниже при описании подпрограмм ввода и расчета аффинных параметров. Поскольку обычно известны параметры прямого преобразования, то перед обращением к KIAFFI необходимо воспользоваться функ- функцией KIAFOB, которая сделает требуемый пересчет. Для обоснования второго, более сложного алгоритма вспомним, что значение любого пикселя фактически характеризует не точ- точку, а некоторую небольшую (обычно квадратную) площадку. Не отвлекаясь на обсуждение принципов получения цифровых изо- изображений, отметим, что это значение обычно получено интегри- интегрированием светового потока (либо другого физического сигнала) в пределах упомянутой площадки. Рассмотрим такую элементарную площадку, отвечающую не- некоторому пикселю нового изображения. Как выглядит прообраз
4.2. Программы аффинного преобразования 133 этой площадки и как он располагается на сетке пикселей исход- исходного изображения? В случае общего аффинного преобразования квадраты превращаются в параллелограммы; в случае преобразо- преобразования подобия квадраты переходят в квадраты (возможен пово- поворот и изменение масштаба). Как правило, прообраз элементар- элементарной площадки нового изображения перекрывается с несколькими пикселями исходного поля. В простейшем алгоритме учитывается только один, ближайший пиксель исходного поля, однако можно модифицировать алгорр ^м так, чтобы все они вносили свой вклад. Основная идея такой модификации состоит в том, чтобы учесть вклады отдельных пикселей с весами, пропорциональными площа- площадям перекрытия. Точный расчет этих площадей возможен, но сильно осложнен необходимостью учета многочисленных частных случаев взаимно- взаимного расположения площадок. Эффективная реализация второго ал- алгоритма основана на приближенном учете площадей перекрытия. Для этого элементарная площадка, соответствующая обрабатыва- обрабатываемому пикселю нового изображения, разбивается на мелкие ква- квадратные ячейки. Количество ячеек такой мелкой сетки определя- определяет точность оценки площадей. Для каждой ячейки вычисляется ее прообраз на исходном изображении. Полученные координаты, округленные, как и в предыдущем алгоритме, до целых чисел, ука- указывают на определенные пиксели исходного поля. Практически, при программировании алгоритма, веса реализу- реализуются тем, что каждый пиксель исходного изображения учитыва- учитывается в образуемой сумме столько раз, сколько ячеек мелкой сетки попало на соответствующую площадку. Сумма затем делится на число ячеек мелкой сетки, и полученное среднее записывается в текущий элемент. По своему внутреннему устройству вторая подпрограмма ана- аналогична первой, но несколько сложнее. Для обработки ячеек мел- мелкой сетки добавлены два внутренних цикла. С целью ускорения вычисления координат прообраза текущей ячейки эти координаты представлены в виде суммы двух компонент: «крупной», ответ- ответственной за перемещение по пикселям нового поля, и «мелкой», дающей смещения при обходе мелкой сетки. Эти смещения вычи- вычислены заранее и помещены в специальные массивы. Подпрограмма KIAFFS имеет 9 параметров; первые 7 совпа- совпадают с параметрами подпрограммы KIAFFI. Добавлены входные параметры mel и рг. Первый определяет кратность мелкой сетки, т. е. число ячеек, приходящихся на один пиксель. Значение mel = 1 соответствует отсутствию мелкой сетки. Значение mel = 8 дает 8 х 8 = 64 ячейки мелкой сетки. Параметр рг позволяет вклю-
134 Глава 4. Геометрические преобразования чать или отменять вывод на экран информации о ходе обработки. Если рг не равно нулю, на экран выводится номер обрабатываемой в данный момент строки изображения. Значение рг = 0 отменяет этот вывод. Контроль особенно полезен при больших значениях параметра mel, когда работа подпрограммы замедляется и вычи- вычисления длятся десятки секунд, что бывает при обработке больших изображений. Сравнение двух описанных алгоритмов позволяет сделать неко- некоторые рекомендации. Первый, более простой алгоритм, очевидно, работает быстрее. Второй выполняется медленнее, и это замедле- замедление примерно пропорционально количеству ячеек мелкой сетки, т. е. квадрату параметра mel. Первый алгоритм сохраняет рез- резкие перепады уровней, второй — наоборот, приводит к их размы- размыванию. Первый алгоритм может приводить к разрывам тонких линий и к исчезновению мелких деталей, второй — позволяет их сохранить, но с ослаблением контраста и в размытом виде. Поэто- Поэтому первый алгоритм можно рекомендовать для обработки изобра- изображений значительных размеров, когда быстродействие становится определяющим требованием, и в случае бинарных полей. Второй алгоритм можно использовать для обработки грубо продискрети- зированных небольших изображений. Кроме того, второй алго- алгоритм предпочтительнее с точки зрения качества преобразованно- преобразованного изображения в тех случаях, когда преобразование сопряжено со значительным изменением масштаба. Листинг 4.1. Функции KIAFFI, KIAFFS, KIAFOB «include <stdio.h> /* ФАЙЛ — KIAFFI.С */ «include "IMAGE.Ни «include "KIAFF.H" void KIAFFI (One,Hi,Nj,Two,Hu,Hv,rap) SHOW One[], Two[]; int Ni,Nj,Nu,Nv; float rap[7]; /* - */ /* АФФИННОЕ ПРЕОБРАЗОВАНИЕ ИЗОБРАЖЕНИЯ по ближайшему дискрету One - исходное поле размером Ni * Mj элементов Two - преобразов&ное поле размером Ни * Nv элементов rap - параметры ОБРАТНОГО преобразования Two —> One */
4.2, Программы аффя^к--;-мфеобгч?*)»**.*^*! 135 COUNT к; int u,v, i?j; float b,e, ui,vj, Si,Sj; b » rap[2]; ui * rap[3]+0.5; e ¦ rap[5] ; vj » rap[6]+0.5; /¦ Сканируем выходное поле */ k=0; fox (u~i; u<«Nu; u++) { Si = ui 4* rap[i] ; Sj * vj += rap[4]; for ('/---I; v<-Vv: v++, M+) { Si += b; Sj +* e; i - (int) Si; /* C*»j) на поле One — прообраз (u,v) */ j = (int) Sj; if ( i<l li i>Ni II j<l M j>Nj ) Two[k] ¦ NODATA; else Two[k] « 0ne[ MARK,(i,j) ]; void KIAFFS (On©,Mi,Hj,T«o,Nu,Nv,rap,mel,pr> SHOW One [] , T:^o [] ; int Ki.NjДи,М7 float rap[7]; АФФИННОЕ ПГЕС*??А303А1П?Е Н30ЕРД1ЕНЙЯ по ближайшему дискрету или р^збийннеп на. подклеткн One - исходное поле размером Ni * Mj элементов Two - преобразованное поле размером Ни * Nv элементов rap - параметры ОБРАТНОГО преобразования Two —> One ffiel - число ячеек мелкой сетки (от 1 до 8) рг - протокол: 0 - нет, 1 - есть ¦/ ^^^^^ям^^^^^^^""***м*^**'**ла'мр*^^ ^ "«¦"'¦¦'«««••••¦•••"•••"•"••¦¦•••"¦"••и ^ # COUNT k; int u,v, i,j, ku,kv, kol,jsm; float A,b,e, ui,vj, Si,Sj,ssi,ssj; float Aui[9], Auj[9], Avi[9], Avj [9] ; if ( iel<l it mel>8 ) { printf ("KIAFFS? Число ячеек сетки mel«%d",Bel); STOP. } b ¦ rap[2]; ui * rap[3]40.5; e * rap[5]; vj « гар[б]40.5;
136 Глава 4. Геометрические преобразования if ( а«1>1 ) /* Подготовка таблицы для мелкой сетка */ for (k«l; к<«в«1; к++) { А«(к-0.Б)/в«1-0.5; Aui[k] »A*rap[l]; Auj [к] * А«тар[2] ; Avi[k] ¦ А*гар[4]; Av j [к] - А*гар[5] ; /* Сканируем выходное поле */ к-0; for (u»l; u<»Nu; u++) { Si ¦ ui +¦ rap[l]; Sj * vj ¦» rap[4]; for (v»l; -<»Hv; v-м-, k++) { Si ¦* b; Sj ¦* e; Two[k]«NODATA; i ¦ (int) Si; /* d»j) на поле One — прообраз (u,v) */ j » (int) Sj; if ( i<l || i>Ni || j<l || j>Nj ) continue; if (ве1»«1) { Two[k] » One[ MARK.(i,j) ]; continue; } /* Обработка мелкой сетки */ kol ¦ 0; /* Текущее число обработанных ячеек */ jam ¦ 0; /* Накопленная сумма значений */ for (ku»l; ки<»ве1; ku++) { esi«Si+Aui[ku]; ssj*Sj+Avi[ku]; for (kv»l; kv<»mel; kv++) { i « (int) ssi+Auj[kv]; j » (int) 8sj+Avj[kv]; if ( i<l II i>Hi || j<l || j>Hj ) continue; j8H +» 0ne[ MARK_(i,j) 1; if ( kol>«Bel ) Two[k] » (jsB+kol/2)/kol; } if ( pr ) { printf("Строка %d\r",u); fflush(stdout); } > if ( pr ) puts (»»>; int KIAFOB (M,W) float M[7] ,W[7] ; /* Получение параметров обратного аффинного преобразования */ /* М - параметры прямого преобразования */ /* Ы - РЕЗУЛЬТАТ: параметры обратного преобразования */ /* _______ __ ___________ _ __—«_-,-._-._„«._„•„____-.«„__««.__-. */ { float eps * 1.0е-8; float a«M[l], Ъ=М[2], с=М[3], d=M[4], e=M[5], f=M[6]; float det = a*e - b*d;
4.3. Ввод аффинных параметров 137 И[0] ¦ 0.0; if ( (det > -ере) кк (det < ере) ) { puts (" KIAF0B: нет обратного преобразовали*!"); Н[1] ¦ И[2] » И[3] » W[4] » Н[5] » W[6] - 0.0; PAUSE. return 0; } И[1] » e/det; И[2] » -b/det; И[3] » (b*f-e*c)/det; Н[4] » -d/det; И[5] » a/det; И[б] » (c*d-a*f)/det; return 1; 4.3. Ввод аффинных параметров Для интерактивного ввода аффинного преобразования служат подпрограммы KIAPAR и KIAPPD, показанные на листинге 4.2. Как уже говорилось, абстрактный характер параметров общего аффинного преобразования заставляет искать способы их нагляд- наглядной интерпретации. Проще всего пожертвовать общностью и огра- ограничиться, например, преобразованием подобия D.6), которое име- имеет 4 параметра. Два из них предельно наглядны: это угол поворо- поворота и масштаб. Оставшиеся два, си/, трактуются как положение центра старой координатной системы в новых координатах. Мы несколько обобщим преобразование подобия, разрешив анизотропное (зависящее от направления) изменение масштаба: и — k\xcosw 4- &22/smty-b с, v = — kixsinw + &2?/costt>-j~ /. D-8) Здесь параметры к\, кч получают естественную интерпретацию как коэффициенты масштаба по осям старого изображения. Ины- Иными словами, выражения D.8) подразумевают, что сначала выпол- выполняется масштабирование, затем — поворот. Сделанное нами об- обобщение особенно удобно тем, что композицией преобразований D.8) можно получить любое аффинное преобразование, о чем бу- будет сказано ниже. Остановимся на способах задания канонических параметров с и /, определяющих перенос начала координат. Их можно указать непосредственно, однако в ряде случаев возможен более удобный ввод. Так, если преобразование содержит поворот и/или масшта- масштабирование, то можно пользоваться координатами неподвижной точки преобразования. Эти координаты связаны с параметрами си/ соотношениями D.4) и D.5). При этом надо помнить о част- частных случаях, когда либо не существует ни одной неподвижной
138 Глава 4, Геометрические преобразования точки (плоскопараллельный сдвиг), либо существует множество неподвижных точек (неподвижная прямая). При тождественном преобразовании все точки — неподвижные. Бели вы хотите, чтобы после преобразования картинка не ухо- уходила за пределы поля, следует оставить неподвижным (зафикси- (зафиксировать) центр поля. При одинаковых размерах исходного и пре- преобразованного изображений желаемый эффект можно получить, задав неподвижную точку в центре. Если исходное т*. преобра- преобразованное изображения имеют разные размеры, надо потребовать, чтобы центральная точка исходного поля пала нейтрал <-ofi точ- точкой преобразованного, и из этого условия рассчитать параметры си/. Для ввода параметров преобразования D.8) предназначена под- подпрограмма KIAPPD. Ввод происходит в диалоговом режиме, при- причем возможны, по выбору, все три варианта: непосредственное указание сдвигов по осям, ввод координат неподвижной точки и фиксированный центр. Вычисленные параметры аффинного пре- преобразования сразу отображаются на экране. Диалоговый ввод чи- чисел типа float осуществляется функцией KDVFLO (листинг 4.3), устроенной аналогично часто применяемой нами KDVINT. Подпрограмма KIAPPD имеет 5 ппрамегров, среди котсрых 4 входных — размеры исходного поля и n-^мепы преобразованного поля, необходимые для расчетов в случае, когда требуется «фик- «фиксированный центр». Выходным является массив, содержащий па- параметры аффинного преобразования. Этот массив со-поит из 9 элементов типа float и устроен следующим образом. Нулевой эле- элемент, раг[0], служит индикатором неподвижной точки. Если раг[О] = 0, то неподвижной точки (единственной) не существует, ина- иначе — она есть. Элементы массива с 1 по б содержат 6 параметров аффинного преобразования: а, 6, с, d, е,/. Последние два элемента, раг[7], раг[8], определены, если раг[0] ф О и содержат в этом случае координаты неподвижной точки. Рассмотренная выше подпрограмма KIAPPD обеспечивает на- наглядность и удобство ввода за счет ограничения общности и по- позволяет использовать пять степеней свободы аффинного преобра- преобразования из шести. Более общим является путь, основанный на представлении требуемого преобразования произведением, или последовательным выполнением, ряда других, возможно, более простых преобразо- преобразований. Например, известно, что любое аффинное преобразова- преобразование общего вида можно разложить на поворот, масштабирование и сжатие к некоторой прямой. При выборе этого пути следует учи-
4.3. Ввод аффинных параметров 139 тывать, что произведение преобразований немультипликативно и результат зависит от порядка их выполнения. Расчеты выполняются по формулам: А' = аА + ЬД В' = аВ + ЬЕ, С = аС + bF + с, ' = dC+eF + f. D.9) Здесь а, 6, с, d, e, / — параметры очередного сомножителя, добавля- добавляемого в цепочку аффинных преобразований. Л, В, С, D,E>F — паг раметры результирующего аффинного преобразования: без штри- штрихов — старые, со штрихами — новые. Такой способ реализован в подпрограмме KIAPAR, которая по- позволяет либо задать новое преобразование, либо добавить преобра- преобразование в цепочку. Для диалогового ввода преобразования вызыва- вызывается описанная выше подпрограмма KIAPPD. Параметры введен- введенного преобразования отображаются на экране. Имеется возмож- возможность повторить ввод при необходимости. Подпрограмма KIAPAR имеет такой же набор параметров, как и KIAPPD. Техника диалога, примененная в описанных выше подпрограм- подпрограммах ввода аффинных преобразований, не является, конечно, един- единственно возможной. Более эффектной была бы организация вво- ввода по типу электронной таблицы или с использованием экранных форм, заполняемых (редактируемых) оператором. Мы не стали за- загромождать изложение такими программами, предоставив их раз- разработку читателям. Листинг 4.2. Функции KIAPAR, KIAPNC, KIAPPD, KIATVF, KIATVS «include <stdio.h> /* ФАЙЛ — KIAPAR.С */ ¦include <math.h> «include "IMAGE.H" «include "KIAFF.H" «include "KDV.H" «define Npr 9 /* Длина, вектора, аффинных параметров */ void KIAPAR (Mi,Mj, Ni.Nj, pax) int Mi,Mj; /* Размер исходного поля */ int Ni,Nj; /* Размер преобразованного поля */ float par [ Npr ];
140 Глава 4. Геометрические преобразования /* Диалоговый ввод параметров преобразования подобия, */ /* мх пересчет в параметры аффинного преобразования */ /* н добавление к имевшемуся аффинному преобразовании */ /ф ¦/ { int j; static int jf"l; static char bus«'Y'; puts ("\n Диалоговый ввод параметров аффинного преобразования"); LB: puts("M); puts("\t 1 - задать новое преобразование"); puts("\t 2 - добавить преобразование"); KDVIHT ("Укажите режим", *jf, 1,2); if (jf«*l) { puts (u\n\t3aj5aeM новое преобразование"); KIAPPD (Mi,Mj, Ni,Nj, par); } else { float M[Hpr]; float A»par[l], B»par[2] , Opax[3], D=par[4], E=par[5], F=par[6]; puts ("\n\t3anaeM преобразование - добавку"); KIAPPD (Mi.Mj, Hi.Hj, M); par[l] « M[1]*A ¦ M[2]*D; par[2] « M[1]*B ¦ M[2]*E; par[4] « M[4]*A + M[5]*D; par[5] « M[4]*B ¦ M[5]*E; par[3] « M[1]*C ¦ M[2]*F ¦ M[3]; par[6] « M[4]*C ¦ M[5]*F ¦ M[6]; puts ("\n Преобразование добавлено в цепочку."); KIATVS (" Итоговые параметры преобразования:",par); if ( KDVYES("Ilpeo6pa3OBaHHe Вас устраивает?",fcbus) ) return; goto LB; void KIAPPD (Mi,Mj, Ni,Nj, par) int Ni,Nj; /¦ Размер исходного поля */ int Ni,Nj; /¦ Размер преобразованного поля */ float par [ Npr ];
4.3. Ввод аффинных параметров 141 /¦ /* /¦ /* Диалоговый ввод параметров преобразования подобия и их пересчет в параметры аффинного преобразования float eps*le-6; float a,b, d,e, kp, ce,en; */ */ */ */ static float kappa*0.0; static float ki«1.0; static float kj«l.^, static float Xn»0.0,Yn»0.0; static float c»0.0, f»0.0; int frm; static int jf«1; /* Угол поворота, град. */ /* Масштаб по вертикали */ /* Масштаб по горизонтали */ /* Непод.точка: строка и столбец*/ /* Начало старой системы */ /* координат в новых координатах*/ KDVFL0 ("Поворот (по ч.с), градусов", fckappa, 0.,0.); KDVFLO ("Масштаб по горизонтали", *kj, 0.1,10.0); KDVFLO ("Масштаб по вертикали", *ki, 0.1,10.0); kp > kappa * 3.14159 / 180.0; "¦ ¦ cos (kp); en ¦ sin (kp); ki * cs; b ¦ kj * en; -ki * en; e ж kj * cs; /* поворот в радианах */ cs a d frm > fabs(kappa)>eps 11 fabs(ki-1.0)>epe II fabs(kj-1.0)>epe; puts ("\n Форма ввода параметров сдвига:"); puts ("\t 0 - каноническая, с указанием сдвигов по осям"); puts ("\t 1-е неподвижным центром (вопросов не будет)"); if (frm) puts ("\t 2-е неподвижной точкой (попросит задать ее)"); KDVIHT ("Укажите форму ввода", tjf, 0,(frm)?2:l); if ( jf!»1 ) puts (""); if (jf—1) { с » (Ni - a*Mi - b*Mj)/2; f * (Nj - d*Mi - e*Mj)/2; } else if (jf«2) { KDVFLO ("Координаты неподвижной точки: - строка", fcXn,0.,0.); KDVFLO ("- столбец", *Yn, 0.,0.); с « (l-a)*Xn - b*Yn; f » (l-e)*Yn - d*Xn; else { KDVFLO ("старое начало координат - строка", fee, 0.,0.); KDVFLO (¦¦- столбец", *f, 0..0.);
142 Глава 4. Геометрические преобразования раг[1] ¦ а; раг[2] ¦ Ь; раг[3] ¦ с; par [4] ¦ d; par [Б] ¦ е; par [6] ¦ f; KIATVS (" Заданы параметры:",par); /* Сохраним старые координаты неподвижной точки в (Xn.Yn): ¦/ /¦ а) если иепод.точка была задана явно, или */ /* Ь) если иепод.точки (единственной) не существует. */ /* Если непод.точка существует и была вычислена, */ /* запишем в (Xn,Yn) ее новые координаты. */ if ( jf !* 1 кк раг[0] > 0.0 ) { Хп - раг[7]; Yn - раг[8]; } } void KIAPNC (par) float par [ Npr ]; /¦ ¦/ /* Расчет положения неподвижной точки */ /* Если неподвижной точки (единственной!) нет, */ /* то индикатору раг[0] присваивается нуль. */ /* Если неподвижная точка есть, то раг[0] > 0. */ /* Тогда par[7] и par[8] - ее координаты: */ /* строка и столбец (не обязательно целочисленные). */ /¦ */ { float dt, epe»le-7; float a«par[l] ,b»par[2] ,c»par[3] , d»par[4],e»par[5],f*par[6]; dt * A.0-a)*A.0-e) - b*d; if ( (dt>-eps) kk (dt<eps) ) par[0] ¦ 0.0; /* неподвижной точки нет */ else { par[0] «7.0; /* неподвижная точка есть */ par[7] « (c*A.0-e)+b*f)/dt; par[8] * (f*A.0-a)+d*c)/dt; void KIATVF (Fout,me8sa,par) FILE «Fout; char «messa; float par [ Npr ];
4.3. В йод аффинных параметров 143 /* Вывод параметров аффинного преобразования в файл */ /* Fout - указатель на файл вывода */ /* иеваа - сообщение (заголовок таблицы параметров) */ /* par - массив параметров аффинного преобразования */ /ф .. */ float a*par[1],I«? ir[2],с*раг[3], d*par[4],е*раг[5],f«par[б]; fprintf (Fout, "\n У.з\п", messa); KIAPNC (par); /¦ координаты неподвижной точки */ if ( par[0] «¦ 0.0 ) /* неподвижной точки нет */ fprintf (Fout, " a ='/.8.4f, b ='/.8.4f, с =y.8.4f\n'\a,b,c); fprintf (Fout, " d «%8.4f, e *y.8.4f, f =y.8.4f\n",d,e,f); else { /¦ неподвижная точка (Xn,Yn) */ float Xn=par[7], Yn=par[8]; fprintf (Fout, " a *y.8.4f, b *'/.8.4f, с =y.8.4f; Xn «У.6.2f\n'\ a,b,c,Xn); fprintf (Fout, " d =X8.4f, e *'/.8.4f, f *y.8.4f; Yn *y,6.2f\n", d,e,f,Yn); fprintf (Fout,"\nn); void KIATVS (messa,par) char *mes8a; float par [ Npr ]; /* Вывод параметров аффинного преобразования на экран */ /* */ { KIATVF (stdout,messa,par); } Листинг 4.3. Функция KDVFLO •include <stdlib.h> •include <string.h> •include <stdio.h> /* ФАЙЛ — KDVFLO.С */ •include <math.h> •include "KDV.H"
144 Глава 4. Геометрические преобразования void KDVFLO (char «text, float *Ev&r, float Emin, float Евах) /* Диалоговый ввод действительной переменной */ /¦ с учетом предыдущего значения */ /¦ Если Eain >¦ Евах, контроль границ отюшчается */ /* text - заголовок, до 40 символов */ /* Evar - вводимал переменная */ /* Ев1п,Евах - нижняя и верхняя границы */ float eold * *Evar; char digit[16]; for ( ; ; ) { printf (541.40s X8.3f » ".text.iEvar); gets (digit); if (strlen(digit)) *Evar ¦ atof (digit); if ( (Eein >» Евах) I| (*Evar>«EBin fcfc *Evar<»E»&x) ) return; ¦Evar ¦ eold; printf ("XlSs Допустимо от )j8.3f до )j8.3f \пи," M,EBin,EBax); 4.4. Вспомогательные подпрограммы Подпрограмма KIATVF служит для вывода параметров аффинно- аффинного преобразования общего вида в заданный файл. Если преобра- преобразование имеет неподвижную точку, то выводятся и ее координа- координаты. Для их расчета используется вспомогательная подпрограмма KIAPNC. KIATVF имеет три входных параметра: указатель на файл вывода; указатель на строку символов, содержащую заголо- заголовок, которым снабжается выведенная таблица параметров; мас- массив, в котором хранятся параметры аффинного преобразования общего вида. Подпрограмма KIATVS отличается от KIATVF тем, что вывод происходит не в файл, а на экран. Внутреннее устройство KIATVS отличается исключительной простотой, так как сводится к вызову KIATVF с первым параметром, задающим вывод на экран. Тексты подпрограмм KIATVF и KIATVS приведены на листин- листинге 4.2 вместе с KIAPAR и KIAPPR. Там же приведена подпрограм- подпрограмма KIAPNC. Подпрограмма KIAPNC рассчитывает координаты неподвиж- неподвижной точки. Она имеет один параметр — массив par. Расчеты
4.5. Тестовые программы 145 проводятся по формулам D.4). Бели неподвижная точка (един- (единственная) существует, то ее координаты записываются в элементы массива раг[7] и раг[8]. Элементу раг[0] присваивается значение 7, если неподвижная точка существует, и 0 в противном случае. Для расчета параметров обратного аффинного преобразования служит подпрограмма KIAFOB (листинг 4.1), имеющая два пара- параметра — массивы М и W. Первый содержит коэффициенты исход- исходного аффинного преобразования в ячейках с 1 по 6; нулевая ячей- ячейка не используется. Зо второй массив записываются результаты — коэффициенты обратного аффинного преобразования. Расчеты проводятся по формулам D.3). Если обратное преобразование су- существует, то его параметры заносятся в массив W и KIAFOB воз- возвращает единицу. Если же задано вырожденное преобразование и обратного не существует, то KIAFOB выводит на экран соответ- соответствующее предупреждение и возвращает нуль, Ячейки массива W в этом случае заполняются нулями. 4.5. Тестовые программы Для экспериментов с геометрическими преобразованиями неболь- небольших изображений служит демонстрационная программа KIAF1 (листинг 4.4). Эта программа позволяет формировать тестовое изображение и управлять его преобразованиями в диалоговом ре- режиме. Исходное и преобразованное поля отображаются на экране в символьном виде. Структура программы определяется двумя циклами типа do ... while, вложенными один в другой. Во внешнем цикле вводятся раз- размеры изображений, распределяется память и формируется тесто- тестовое изображение. Затем выполняется внутренний цикл, в котором сконцентрированы процедуры ввода преобразований и обработки изображения. После завершения внутреннего цикла выделенная память освобождается и на экран выводится запрос: «Сформи- «Сформировать новое поле?». При положительном ответе (Y) внешний цикл повторяется. При отрицательном (N) программа заверша- завершает работу» Внутренний цикл начинается с вывода на экран исходного изображения Image. Затем, при положительном ответе на за- запрос: «Задать новое преобразование?», вызывается подпрограмма KIAPAR, управлякмцая вводом параметров преобразования. При отрицательном ответе используется преобразование, заданное в предыдущем проходе цикла. В первом проходе запрос не форми- формируется и KIAPAR вызывается всегда.
146 Глава 4. Геометрические преобразования После того как преобразование задано, для расчета параметров обратного преобразования вызывается подпрограмма KIAFOB. (Если обратного преобразования не существует, управление пере- передается на конец цикла.) Затем вызывается (по выбору) один из двух алгоритмов преобразования. Преобразованное поле выводит- выводится на экран. Внутренний цикл заканчивается запросом: «Повто- «Повторить с тем же полем?». При положительном ответе на него цикл выполняется с начала, при отрицательном — выполнение цикла завершается. Листинг 4.4. Тестовая программа KIAF1 «include <stdio.h> /* ФАЙЛ — KIAF1.C */ «include "IMAGE.Ни «include "KIAFF.H" «include "KDV.H" /* Заполнение в вывод матрицы */ /* Аффинное преобразование поля */ void mainО { SHOW ¦Image, *ImAff; static float Pax[9]»{0,l,0,0,0,1,0,0,0}, rap[9]; int Ii-20, Ij-20; int ¦el»l,vpr»l; COUIT nd; char new»'I', rpt»'Y>, do { puts ("\n\nKIAFl: Аффинное преобразование поля"); KDVIIT ("Высота поля, строк Mi",*Ii,1,512); KDVIIT ("Ширина поля, столбцов Ij",«j ,0,512); nd - (COUIT) li * Ij; if O(Image»malloc(nd))) { puts("Нет места для Image!"); STOP. } if (!(ImAff»malloc(nd))) { puts("Нет места для ImAff•"); STOP. } KIMGTP (Image,Mi,Ij,1,8); do { KIOTC (Image,Hi,Hj); PAUSE. if ( newp»»'0> II KDVYESC Заменить преобразование?",knewp) ) EIAPAR (Mi.Ij, Mi.Ij, Par); if ( newp ¦¦ »0» ) newp-'Y'; if ( !KIAFOB (Par,rap) ) continue;
4.5. Тестовые программы 147 puts ("\n Выберите алгоритм преобразования:••); IDVIIT ( - по ближ.дискрету, 2 - ра»биеииеи",*?рг,1,2); if ( трг«»1 ) KIAFFI (lBage,Ii,Ij,lBAff,Ii,Ij,rap); else { KDVIIT ("Ячеек в стороне сетки A*..8)",keel,0,0); KIAFFS (i KIOTC (iBAff.Ii.Ij); } while ( KDVYESC" Повторить с теи же полей?",trpt)); free (IsAff); free (laage); } while ( KDVYESC Сформировать новое поле?", fcnev)); EID. Сценарий общения с программой выглядит следующим обра- образом. После вызова программа выводит заголовок: «KIAF1 — Аф- Аффинное преобразование поля» и запрашивает размеры изображе- изображения — число строк и столбцов. В данной программе размеры ис- исходного и преобразованного полей совпадают. Бели затребованы изображения чересчур большого размера, то программа не сможет разместить их в памяти и остановится. При этом появится сооб- сообщение «Нет места для Image», если не удается разместить исходное поле, или «Нет места для ImAff», если исходное поле размещено, но не хватает места для второго поля. После размещения полей в памяти формируется и выводится на экран тестовое изображение типа «рама». Затем программа требует ввести параметры аффинного преобразования, предлагая на выбор два режима: 1 — задать новое преобразование и 2 — добавить преобразование. В режиме 2 введенное преобразование добавляется к заданному ранее. Этот режим позволяет задать про- произвольное аффинное преобразование. Первоначально при запуске программы устанавливается единичное (тождественное) преобра- преобразование. Ввод параметров осуществляется в терминах масштабов по ко- координатам, угла поворота и сдвигов по осям или координат не- неподвижной точки. Параметры, введенные ранее, сохраняются в статической памяти и предлагаются в следующем сеансе ввода. Это облегчает их коррекцию, позволяя добиваться требуемого ре- результата при меньшем числе нажатий клавиш. После завершения ввода на экране появляется таблица с параметрами общего аф- аффинного преобразования. Бели преобразование вас не устраивает, можно повторить ввод параметров.
148 Глава 4. Геометрические преобразования Вычисление обратного преобразования сопровождается про- проверкой на корректность. Если вы ухитрились задать преобразо- преобразование, которое не имеет обратного, появится запрос на продол- продолжение работы и ввод новых параметров преобразования. Если преобразование задано корректно, программа предлагает выбрать один из двух алгоритмов: 1 — по ближайшему дискрету или 2 — с разбиением на подклетки. При выборе второго алгоритма тре- требуется задать кратность мелкой сетки. Кратность задается чи- числом (от 1 до 8), которое определяет, сколько ячеек мелкой сетки укладывается в стороне ячейки исходного пикселя. Понятно, что при единичной кратности второй алгоритм превращается в пер- первый. Далее происходит, собственно, преобразование поля. Если был выбран второй алгоритм, то на экране динамически индицирует- индицируется номер обрабатываемой строки изображения. По завершении обработки преобразованное изображение выводится на экран. Затем программа спрашивает: «Повторить с тем же полем?». Если ответить положительно, на экране появляется сформирован- сформированное ранее тестовое поле типа «рама» и повторяется описанная выше обработка, включающая ввод преобразования, выбор алго- алгоритма, выполнение преобразования и просмотр результатов. При отрицательном ответе программа спрашивает: «Сформировать но- новое поле?». В случае положительного ответа на этот запрос выпол- выполнение программы повторяется с начала (с вывода заголовка), при отрицательном — выполнение прекращается. На рис. 4.1 и 4.2 показаны результаты преобразования тестово- тестового поля типа «рама», изображенного на рис. 1.5. Поля преобра- преобразованы с помощью подпрограмм KIAFFI (преобразование по бли- ближайшему дискрету) и KIAFFS (преобразование с разбиением на подклетки). Использованы следующие данные: угол поворота: 30.0°; масштаб по горизонтали: 0.8; масштаб по вертикали: 1.0; неподвижная точка — строка: 10.0; — столбец: 11.0. Этим данным соответствуют следующие параметры аффинного преобразования: а = 0.8660, b = 0.4000, с = -3.0603, d = -0.5000, е = 0.6928, / = 8.3790.
4.5. Тестовые программы 149 О 5 10 15 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1 •.—.•.«pa.** • © © • • • • 11..„«¦—. ,1. .888. ...11 „.«.„.«. 1 • .88* i 8 о i • • • 1 1 _ . . • • 1 • о о i • • • о о о • • • • 1 _«,_!. е 8 8 . • •888.88. • . _ _ «. • .88. • • • 8 8 8 . о 8 • • • „. .. 1 • .88. . .888. .88. . . „ 1 • . 8 8 8. .88* .88* * «1 . 1 . .8. .88888. .88. . 1 . 1 . .88. . * .888888. . » 1 „ 1 .88» . . .88.888. * . 1 _ «. • . .88. .88* * . о о . . • 1 _ _ • • • *88.88. *8Ог • • 1 _ „ «„ 88 ... 88 .. 1 1 1 8888. ..1 . ..11. 88. .1 11 1 .„ 11 1 11.... 1 Рис- 4.1- Тестовое поле, преобразованное с помощью алгоритма «по ближайшему дискрету». На преобразованных изображениях видны участки, отсутство- отсутствовавшие на исходном поле, лежащие за его пределами. Подпро- Подпрограммы, выполняющие аффинные преобразования, заполняют со- соответствующие пиксели преобразованного поля уровнем N0DATA, который при печати отображается в виде знака подчеркива- подчеркивания. Программа KIAF2 (листинг 4,5) аналогична KIAF1, но имеет два отличия. Во-первых, размеры исходного и преобразованного полей не обязаны совпадать, так как задаются независимо друг от друга. Во-вторых, результаты выводятся не только на экран, но и в файл с именем €KIAF2.DAT». Вывод происходит при положи- положительном ответе на соответствующий запрос. Вместе с преобразо- преобразованным полем выводятся параметры соответствующего аффинно- аффинного преобразования (и параметры обратного преобразования). Для вывода в файл полей используется подпрограмма KIOFPG, для вывода параметров прямого и обратного аффинного преобразова- преобразования — подпрограмма KIATVF.
150 Глава 4. Геометрические преобразования ( 1 ! 2 ! 3 ! 4 ! 5 ! 6 ! 7 ! 8 ! 9 ! 10 ! 11 ! 12 ! 13 ! 14 ! 15 ' 16 17 ' 18 19 20 > - - 1 . 1 . . 1 . 1 . . ! 1 . 4 ' . . 4 1 . . 2 ! 1 . . ! - - 1 1 1 2 5 7 7 3 # 1 5 * 1 1 5 7 8 3 4 4 2 mm 1 1 1 5 6 8 5 3 # 5 3 1 1 1 1 # 4 5 8 7 5 4 # # 2 6 3 1 2 5 7 8 3 1 4 4 2 . 4 5 8 4 1 1 3 6 8 3 2 # 5 4 5 8 6 4 ш 5 3 10 ¦» 1. 4 5 4 2 6 8 8 3 2 4 4 2 5 3 1 5 6 8 8 6 1 # 9 5 8 3 1 2 4 3 4 5 8 8 8 7 6 3 4 5 8 6 2 1 5 5 7 8 8 4 3 5 7 6 7 8 3 1 1 1 2 5 8 6 3 1 # 6 8 8 3 2 mm 1 1 15 " 1 . 4 5 4 # 4 6 8 4 3 # 9 1 1 1 . 5 4 5 8 6 3 1 # # 9 1 1 # 4 6 8 3 2 # # 1 20 111 . 1 1 . . • 4 . . 2 . . . . 1 • • — . 1 I 1 . . 1 . . Рис. 4.2. Тестовое поле, преобразованное с помощью алгоритма «разбиение на подклетки», кратность мелкой сетки — 3. Листинг 4.5. Тестовая программа KIAF2 «include <stdlib.h> •include <8tdio.h> /* ФАЙЛ — KIAF2.C */ •include "IMAGE.H" •include "KIAFF.H" •include "KDV.H" /* Заполнение и вывод матрицы */ /* Аффинное преобразование поля */ char fit[] » "KIAF2.DAT"; /* имя файла вывода */ void «ain() SHOV *I*age, *IeAff; static float Par[9]*{0,l,0,0,0,l,0,0,0}, rap[9]; int Mi»20, Mj»20, ii.Ij; int ¦•1>тргв1; COUIT ndM, ndl;
4.5. Тестовые программы 151 char cfi»'I\ ctt»'Y\ new«>I>, rpt»'Y\ ne*p»>0>; FILE «Fout, *fopen(); puts ("\n\nKIAF2: Аффинное преобразование поля\п"); if ( KDVYES ("Сохранять результаты в файле?", fccfi) ) { if ( cfi шш 'у' ) cfi ¦ 'Y>; Fout ¦ fopen (fit,"*"); if (Fout) printf ("XtBuBOA результатов в файл %s\n", fit); else { printf ("Файл %в не открывается!", fit); STOP. } } puts </-^\пУкажите размеры исходного изображения"); KDVIHT ("Высота поля, строк Hi",kMi,1,512); Hi»Mi; KDVIIT ("Ширина поля, столбцов Mj",*Mj,0,512); Hj«Mj; ndM « (COUHT) Mi ¦ Mj; if (!(Image»malloc(ndM))) { puts("Нет места для Image!"); STOP. } KIMGTP (Image,Mi,Mj,1,8); if ( cfi««'Y' kk KDVYES ("Сохранить это поле в файле?", *ctt) ) KIOFPQ (Fout,0,Image,Mi,Mj, l,Mi, l,Mj, 2,79,60); do { puts ("\t\nУкажите размеры преобразованного изображения"); KDVIHT ("Высота поля, строк Hi",*Hi,1,512); KDVIHT ("Ширина поля, столбцов Hj",*Hj,0,512); ndH » (COUHT) Hi ¦ Hj; if (!(ImAff*malloc(ndH))) { putsC'HeT места для ImAff!"); STOP. } do { KIOTC (Image,Mi,Mj); PAUSE. if ( nevp»s>o> || KDVYES("Заменить преобразование?",ftnevp) ) KIAPAR (Mi,Mj, Hi.Hj, Par); if ( newp ¦» '0* ) newp*'Y'; if ( IKIAFOB (Par,rap) ) continue; puts ("\n Выберите алгоритм преобразования:'1); KDVIIT ( - по ближ.дискрету, 2 - разбиением",fcvpr,l,2);
152 Глава 4. Геометрические преобразования if ( vpr««i ) KIAFFI <Ieag«uHi,Mj, IaAff.Ii.Ij, rap); elee { KDVIIT ("Ячавк в стороне свткн (t...8)'\ tael, 0,0); KIAFFS (Ieage.Mi.Hj, leAff,*i,»j, rap, m^I); > KIOTC Q«Aff,Ii,Ij); if ( cfi»*'Y» ft* KDVYES(flCoxpaimT* это пола в фавле?",*с«)) { KIATVF (Font/1 Параметры аффинного зр«о6разоал.ия?",Раг); KIATVF (Fout," Пар&мзтрм обратного преобразования",гар); KIOFPG (Fout,0,IiiAff,lli,Ijt l.Ii, l,Ij, 2,79,60); } nhil« ( KDVYESC" Повторить с тем же полек?",*rpt)); free (I»Aff); } while ( KDVYBSC"изменить размеры второю поля?11, free (laage); if ( cfi «* *Yf ) { fprintf (Fout/'\n\t™ :-:-:-:-:-:-:-:-: -\n"); if (fcloee(Foat) !« 0) |mte ("\n Ошибка эшрыття > EID.. Прототипы функций, описаьных в данной главе, содержатся в файле KIAFF.H (листинг 4.6). Вы можете поместить эти прототи- прототипы в файл KIMAG.Hf тогда файл KIAFF.H не протребуется. Листинг 4.6. Головной файл KIAFF.H /* ФАЙЛ — EIAFF.H */ /* $©жно дзбашть »тй определения к файлу IJHAG.H ¦/ void KIAFFI (SHOW One[], int Mi, int Ij» lu, int Iv, float r vt>id KIAFFS (SHOV OneD, int 11, int Ij, $ШЪ TwoG» i»v> ^wf int tor float .int ¦«!, int pr); int KIAFOB (float M[7],float void XIAPAR (int Hi, int Mj, int Hi, int Mj, float parQ); void KIAPPD (int Hi, int Mj, int fif int Kj, float parQ); void KIAPHC (float parO); void KIATVF (FILE *Fout, char ¦eeesa, float par[J); void KIATVS (char *ae»sa, float par[j);
ЛОКАЛЬНАЯ ФИЛЬТРАЦИЯ ИЗОБРАЖЕНИЯ Данная глава посвящена рассмотрению ряда алгоритмов, объеди- объединенных общей идеей фильтрации скользящим окном, или апер- апертурой. Термин «локальная* подчеркивает тот факт, что размеры окна по обеим осям меньше соответствующих размеров фильтруе- фильтруемого изображения. Цели локальной фильтрации обычно состоят в улучшении ка- иества изображения; чаще всего это устранение помех или повы- повышение резкости, подчеркивание контуров [5, 7]. Локальная филь- фильтрация предоставляет для этих целей достаточно богатый арсе- арсенал средств обработки, от алгоритмов усреднения по окрестности (интегрирования) до операций локального дифференцирования и Подчеркивания контуров, Мы рассмотрим некоторые, наиболее употребительные, алгоритмы фильтрации, линейные и нелиней- нелинейные, простые и рекурсивные. 5.1. Принципы локальной фильтрации Фильтрация изображений имеет много общего с хорошо извест- известной в радиотехнике фильтрацией сигналов [18]. Отличие состоит в том, что вместо «одномерного* фильтра, предназначенного для фильтрации зашумллнного сигнала одной переменной (обычно — времени), дал фильтрации изображений применяются двумерные фильтры, отвечающие двумерной природе самого изображения* Такой двумерпнй фильтр устроен следующим образом. Берет- Берется небольшой, обычно прямоугольный, участок плоскости и на нем определяется некоторая функция. Упомянутый участок называ- называется апертурой, или окном, а заданная на нем функция — ве- весовой функцией, или функцией окна. Таким образом, каждому элементу апертуры соответствует (присваивается пользователем) определенное число, называемое в дальнейшем весовым множите- множителем. Совокупность всех весовых множителей и составляет весовую
154 Глава 5. Локальная фильтрация изображения функцию. Апертура вместе с заданной на ней весовой функцией часто называется маской [1, 5-7]. Немаловажным достоинством последнего термина является его краткость. Апертура обычно имеет небольшой размер, например 3x3 или 5x5 элементов. Линейные размеры апертуры обычно берутся не- нечетными, чтобы можно было однозначно указать ее центральный элемент. Отметим, что иногда применяют «одномерную» апертуру, раз- размер которой по одному из направлений равен 1. «Одномерная» апертура, расположение которой не соответствует осям коорди- координат, может быть сформирована заданием соответствующей весо- весовой функции на обычной, прямоугольной апертуре. Фильтрация осуществляется перемещением апертуры фильтра по изображению. В каждом положении апертуры выполняются однотипные действия, которые и определяют отклик фильтра. Весовая функция в процессе перемещения окна также остается неизменной (стационарный фильтр). Поэтому фильтрацию сколь- скользящим окном относят к пространственно-инвариантным операци- операциям [7]. Характерным примером служит алгоритм линейной фильтра- фильтрации, состоящий в общих чертах в следующем. При каждом по- положении апертуры весовая функция поэлементно умножается на значения соответствующих пикселей исходного изображения; про- произведения суммируются. Сумма делится на нормирующий коэф- коэффициент, и полученная величина, являющаяся откликом филь- фильтра, присваивается тому пикселю нового (профильтрованного) изображения, который соответствует положению центра аперту- апертуры. Центр здесь выбирается потому, что в ином случае (если, скажем, присваивать значение отклика пикселю, соответствующе- соответствующему левому верхнему элементу окна) фильтрация сопровождается нежелательным сдвигом профильтрованного изображения относи- относительно исходного. Упомянутый выше нормирующий коэффици- коэффициент обычно берется равным сумме всех элементов весовой функ- функции (весовых множителей). Обычно, но не всегда. Ниже мы позна- познакомимся с такими весовыми функциями, которые дают нулевую сумму. В таких случаях нормирующий коэффициент принимает- принимается равным единице. Наряду с линейными фильтрами существуют и нелинейные. Характер фильтра зависит от операций, выполняемых в каждом положении окна. В линейных фильтрах отклик является линейной функцией многих переменных, роль которых играют попавшие в окно пик- пиксели. Весовые множители — это коэффициенты упомянутой ли-
S.2. Замечании о движении окна 155 нейной функции. Фильтры, в которых отклик не может быть вы- выражен линейной функцией от значений элементов изображения, являются по определению нелинейными. В зависимости от того, куда (в какое поле) записывается отклик фильтра, различают простые и рекурсивные фильтры. Белиз про- простых фильтрах отклик записывается в выходное изображение, то в рекурсивных он записывается обратно в исходное изображение, изменяя значение пикселей непосредственно в процессе фильтра- фильтрации. Поэтому в рекурсивных фильтрах уже обработанные пиксе- пиксели влияют на результат фильтрации последующих. Рекурсивные фильтры т-иоке рассматриваются в данной главе. Иногда оказывается полезным многократное повторение проце- процедуры фильтрации; в этих случаях говорят об итеративном при- применении фильтров (не путать с рекурсивными фильтрами!). 5.2. Замечания о движении окна Фильтрация требует перемещения апертуры по обрабатываемому изображению. В программах мы используем единственный тип движения, аналогичный прогрессивной телевизионной разверт- развертке. Апертура перемещается вдоль строки слева направо с шагом в 1 пиксель; дойдя до конца одной строки, переходит к началу сле- дующей. Это наиболее привычный, но, конечно, далеко не един- единственный тип движения. Траектория перемещения окна может быть любой, требуется лишь, чтобы центр окна посетил по одному разу все пиксели. С точки зрения результатов фильтрации при программирова- программировании простых (т. е. нерекурсивных) алгоритмов безразлично, какой ten движения, какую траекторию перемещения окна выбрать — результаты от этого не зависят. Тип движения может влиять На другие, характеристики, например на быстродействие алго- алгоритма. Мы не будем рассматривать вопросы, определяющие выбор то- 1*0 или иного типа движения, так как для их решения необхо- необходимо учитывать не только особенности реализуемого алгоритма фильтрации, но и специфику решаемой задачи в целом. Напри- Например, в [27] описан (уже упоминавшийся нами в разд. 3.4) алго- алгоритм, в котором зигзагообразное перемещение окна органически связано с рекуррентным характером вычислений, позволяющим повысить быстродействие. В этом примере тип движения окна определен внутренней структурой использованного алгоритма ана- анализа.
156 Глава 5. Локальная фильтрация изображения В других случаях тип движения окна выбирают на основа- основании внешних (по отношению к алгоритму фильтрации) факторов. Здесь можно было бы привести ряд конкретных примеров, но это далеко выходит за рамки выбранной темы. Рассмотрим теперь другой аспект перемещения окна, который редко упоминается в литературе. Речь пойдет о так называемом краевом эффекте и о способах обработки пикселей, лежащих вбли зи границ поля. Влияние краевых эффектов особенно ощутимо на небольших изображениях. Если размер изображения много больше размера окна, то доля площади профильтрованного поля, на которой за» метны краевые эффекты, мала. В этом случае краевыми эффек- эффектами можно пренебречь и выбирать алгоритм обработки из других соображений, например по максимуму быстродействия. Вопрос о краевых эффектах вряд ли можно назвать принци- принципиальным, однако при программировании его необходимо как-то решать. В этой связи мы различаем 3 схемы обработки, которые будем обозначать латинскими буквами Р, 5, Т. При фильтрации центральной зоны, в которой окно целиком помещается на изобра- изображении, все три схемы дают одинаковые результаты. Выбор схемы обработки краев в принципе никак не связан с типом движения окна, хотя сложность полученной программы может зависеть от их сочетания. Рассмотрим названные три схемы подробнее. Р-схема обработки соответствует случаю, когда окно (ни один его элемент) не может выходить за пределы фильтруемого поля. S-схема обработки разрешает выход краев окна за пределы по- поля, требуется только, чтобы центр окна всегда находился внутри поля. Т-схема обработки соответствует фильтрации на тороидальной поверхности. Р-схема обработки ограничивается фильтрацией центральной зоны, оставляя края изображения необработанными. Так как ре зультат фильтрации записывается в элемент выходного поля, соот- соответствующий центру окна, то ширина необработанной зоны рав на половине размера окна (по соответствующей координате) ми нус единица. Таким образом, фильтрация по Р-схеме приводит к уменьшению фактических размеров изображения на ширину не обработанной каймы. Достоинство Р-схемы — простота програм- программирования. S-схема обработки устраняет указанный недостаток ценой не которого усложнения алгоритма обработки. Так как при этом центр окна пробегает все пиксели исходного поля, то и на про
5 2. Замечания о движении окна 157 фильтрованном изображении не остается незаполненных элемен- элементов. Элементы апертуры, выходящие за границы поля, в обработ- обработке не участвуют, поэтому отклик фильтра формируется на основе меньшего числа пикселей, чем в центральной зоне. Иными слова- ми, вблизи краев изображения фактический размер окна умень- уменьшается. Краевой эффект сохраняется, хотя и в существенно осла- ослабленном виде. Алгоритм, реализующий 5-схему, сложнее и бы- быстродействие его хуже, чем в случае Р-схемы. Для п *ноты картины упомянем другую разновидность 5-схе- мы. В описанном варианте при выходе части элементов окна за пределы изображения осуществляется «усечение» окна. Что если Поступить прямо противоположным образом — не усекать окно, а расширить изображение, доопределив его за пределами своих Границ? Совсем нетрудно написать программу, где изображение цне своих границ полагается тождественно равным нулю. Лучшие результаты, по-видимому, должно дать использование экстрапо- экстраполяции. Мы предлагаем заинтересованным читателям исследовать различные варианты. Третья, Т-схема обработки получается, если представить, что изображение определено не на плоскости, а на тороидальной по- поверхности. Представим, что изображение сначала скручивается в трубку, Так что его правый край примыкает к левому. Обратим внимание на важную деталь: правый край сдвинут вниз относительно левого На один пиксель, так что конец первой строки примыкает не к ее началу, а к началу второй строки и т. д. Теперь соединим верхний Край образовавшейся трубки с нижним, так чтобы за последним Пикселем последней строки шел первый пиксель первой строки. Получим тор, на который спирально намотан одномерный мас- №в, содержащий изображение. Вместо тора можно представить бесконечную плоскость, на которой заданное изображение перио- периодически повторяется. Тороидальная интерпретация заметно упрощает программиро- программирование, так как перемещение центра окна по строкам изображения Заменяется его продвижением по индексу одномерного массива, Содержащего изображение. Т-схема обработки является одной из яучших в смысле быстродействия, однако и она не свободна от краевых эффектов, хотя и другого типа, нежели вР-и S-схемах. Краевой эффект здесь состоит в том, что объекты, находящиеся Йа одном краю изображения, влияют на обработку и оставляют Свой след на противоположном краю, что может показаться про- противоестественным .
158 Глава 5. Локальная фильтрация изображения 5.3. Базовый алгоритм локальной фильтрации Теперь рассмотрим ряд общих вопросов, возникающих при раз- разработке программ фильтрации изображений скользящим окном, Чтобы дальнейшие рассуждения не были чересчур абстрактными, мы возьмем для примера фильтр низких частот. Весовая функция такого фильтра в пределах окна тождествен- тождественно равна единице. Бе можно не задавать в явном виде, а «зашить» в структуру программы, что позволяет сделать некоторые упроще- упрощения при сохранении особенностей, присущих программам локаль- локальной фильтрации. Мы опишем три подпрограммы низкочастотных фильтров — KIFILP, KIFILS и KIFILT, реализующие соответствен- соответственно Р-, 5- и Г-схемы обработки. Движение окна — построчное. Подпрограммы имеют следующие 6 параметров: исходное поле, его размеры по строкам и столбцам, размеры апертуры также по строкам и столбцам и выходное поле. Подпрограмма KIFILP (листинг 5.1) перед обработкой очищает выходное иоле, но только в том случае, если в качестве исходного и выходного изображений указаны различные массивы. Очист- Очистка требуется потому, что Р-схема перемещения оставляет края профильтрованного поля необработанными. В процессе очистки выходное поле заполняется уровнем NODATA как раз для того, чтобы это подчеркнуть. Можно просто обнулить поле, тогда необ- необработанные края поля останутся нулевыми. Упомянутая выше проверка позволяет использовать подпро- подпрограмму в качестве рекурсивного фильтра, предотвращая затирание исходного поля в этом случае. Рассмотрению линейных рекурсив- рекурсивных фильтров посвящен один из разделов данной главы. Основная часть программы состоит из четырех вложенных ци- циклов. Два внешних организуют перемещение окна по изображе- изображению, два внутренних — отвечают за суммирование элементов изо- изображения в пределах окна. Начальное положение окна — в левом верхнем углу поля. Окно перемещается вдоль строки до тех пор, пока его правая граница не достигнет границы поля. Тогда происходит переход в начало сле- следующей строки. Обработка продолжается до тех пор, пока окно не окажется в правом нижнем углу поля. Синхронно с перемещением окна изменяется переменная Jfco, дающая одномерную координату центра окна. Два внутренних цикла заведуют перемещением текущего эле- элемента (i,j) внутри области поля, накрытой окном. Одномерная
$.3. Базовый алгоритм локальной фильтрации 159 координата it текущего элемента вычисляется с использованием вспомогательной переменной its, которая имеет смысл одномер- одномерной координаты первого обрабатываемого элемента текущей стро- строки. Этим удается несколько сократить вычисления, избавившись ОТ необходимости вычислять внутри цикла величину it по формуле A.1). Значения пикселей, попавших внутрь окна, накапливаются в переменной sum. Затем сумма делится на нормирующий коэф- коэффициент winsm, равный здесь количеству элементов окна. Полу- Полученное значение присваивается элементу ко выходного массива. 'tU4.. Листинг 5.1. Функция KIFILP finelude <stdio.h> tinclude "IMAGE.Нм /* ФАЙЛ — KIFILP.С ¦/ void KIFILP (lB&ge,Ii,Nj, Mp,Mq, ImOut) /* Р-схема */ SHOW lB&ge[], I*0ut[]; int Ni,Nj,Mp,Mq; /* Фильтрация скользящим окном * на плоскости. */ /* Перемещение окна - по строкам. */ /¦ Окно НЕ N0IET выходить за пределы поля, */ /¦ поэтому края профильтрованного поля оставтея пустыми! */ /* Image - исходное изображение */ /¦ Hi, Nj - размеры исходного и профильтрованного полей */ /¦ Мр, Mq - размеры окна */ /¦ IaOut - профильтрованное поле (сначала очищается) */ /* Внимание! Края этого поля не обрабатывается! */ /¦ ¦/ /* Результат обработки записывается в тот элемент */ /* поля InOut, который накрывает точка окна (pm,qm). */ /* Это задано оператором (kosMARK_(il+pB-l,qn). */ /* « */ { int register i,j; COUNT k> ko, ks; int il,jl; /* левый верхний угол окна */ int i2,j2; /* правый нижний угол окна */ int рв*(Мр+1)/2, qB»(Mq+l)/2; /* условный центр окна */ int winsBsMp*Mq; /* сумма элементов окна */ int sub; /* Очистка - для нерекурсивного фильтра */ if ( Image !* ImOut ) { for (k»Ii*Ij; k>0; ) IeOutC—k] » IODATA; } /* перемещение окна */ for (il»l,i2»Mp; i2<*Ii; il++, i2++) for (ko»MARK_(il+pB-l,qB>t jl»l,j2»Mq; j2<»Ij; jl++, J2++, ko++)
160 Глава 5. Локальная фильтрация изображена suit « 0; for tk* for Ck»k», j eu* ¦* (int) laOuttko] ¦ (iat) ( (float>*u» / wins» ¦ 0.5 ); /¦ обработка, в окне */ На рис. 5.1 показано тестовое поле, содержащее квадрат на нулевом фоне. На рис» 5,2 приведен результат фильтрации этогс поля подпрограммой KIFILP. Подпрограмма KIFILS (листинг 5.2) устроена несколько слож нее. Очищать выходное поле здесь не требуется, так как все ег элементы получают значения в процессе фильтрации. Два внеш них цикла, как и в предыдущей программе, отвечают за перем щение окна. В начальном положении центр окна находится в левом верхнем ушу поля. Понятно, что часть окна при этом оказывается за пре делами изображения. Окно перемещается таким образом, что его центр последовательно, строка за строкой, пробегает все пиксели '<> е- е10 15 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 777777777 777777777 777777777 7777777 7 7 7 7 7 7 7 7 7 7 7 77777777? 777777777 777777777 7777777 7 7 Рис. 5.1 • Тестовое поле.
5.3. Базовый алгоритм локальной фильтрации 161 5 10 15 20 ..4666664321 ..6777776431 ..6777776431 .. Н 7 7 7 7 7 б 4 3 1 ..6777776431 ..6777776431 ..4666664321 ..3444443321 ..2333332211 ..111111111. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Рис. 5.2. Результаты фильтрации. Алгоритм KIFILP, окно 5x5. исходного поля. Одномерная координата центра ко при каждом перемещении окна увеличивается на единицу. Для текущего положения окна рассчитываются границы до- допустимого изменения координат внутри окна. Эти границы ис- используются в двух внутренних циклах, управляющих суммиро- суммированием элементов, попавших в окно. Площадь окна постоянна только в центральной зоне, а вблизи краев уменьшается; поэто- поэтому при определении нормирующего коэффициента используется условный оператор. В центральной зоне нормирующему коэффи- коэффициенту присваивается заранее вычисленное значение, а вне ее он перевычисляется для каждого положения окна заново. Листинг 5.2. Функция KIFILS «include <etdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIFILS.С ¦/ Ўoid KIFILS (Ift*g«,Ii,Ij, Hp,Hq, IftOut) /¦ S-схема */ SHOV Imaged, IftOut?]; int IitIj,Mp,Mq; /¦ ~—-———— — —- ¦/ /¦ Фильтрация скользящим окном - яа плоскости. */
162 Глава 5. Локальная фильтрация изображения /* Центр окна, пробегает все поле, перемещаясь построчно. */ /* Окно МОЖЕТ выходить за пределы поля, */ /* но обрабатывается только часть окна, лежащая на поле. */ /* Результат обработки записывается в тот элемент */ /* поля IaOut, который накрывает точка окна (qa,qa). */ /* Iaage - исходное изображение */ /* IaOut - профильтрованное поле */ { int register p,q; /* индексы окна (строка., столбец) */ int pl,p2, ql,q2; /* границы изменения индексов окна */ int pz; /* индикатор внутренней зоны поля */ COUXT ks; /¦ то же, в координатах поля */ int io.jo; /¦ текущее положеняе центра окна */ COUXT ко; /* текущее положение центра окна */ C0UIT к; /¦ текущий элемент поля ¦/ int рв*(Нр+1)/2, qi*(Mq+l)/2; /¦ условный центр окна */ int vinsB,Vin"Hp*Hq; /¦ сумма элементов окна ¦/ int sub; for (ko«0,io«l; io<«Xi; io-мО { pi ¦ 1+pe-io; if ( pi < 1 ) pi ¦ 1; p2 ¦ Xi+pe-io; if ( p2 >Hp ) p2 ¦ Hp; pz ¦ (pl**l ** p2««Hp); for (jo«l; jo<«Xj; jo*+, ko-н») ql ¦ 1+qK-jo; if ( ql < 1 ) ql ¦ 1; q2 » Xj+qa-jo; if ( q2 >Hq ) q2 » Hq; vinsa « (pz tt ql*~l kk q2""Nq) ? Win : (p2-pl+l)*(q2-ql+l); sub • 0; ks ¦ HARK_(io-pB*pl,jo-qB*ql); for (p«pl; p<«p2; p*+, ke*«Xj) for (k«ke, q*ql; q<«q2; q*+, k**) sub ¦¦ (int) Iaage[k]; IsOutCko] * (int) ( (float)sub / vinea + 0.5 ); Результат фильтрации тестового поля показан на рис. 5.3. За- Заметен краевой эффект: квадрат после фильтрации становится асимметричным — уровень на границах квадрата, лежащих вну- внутри поля, спадает быстрее, чем на границах, лежащих вблизи кра- краев поля. Подпрограмма KIFILT (листинг 5.3) реализует фильтрацию на торе. Для продвижения окна здесь требуется только один (внеш- (внешний) цикл. Два внутренних цикла, как и раньше, управляют сум- суммированием элементов поля, попавших в окно.
5.3. Базовый алгоритм локальной фильтрации 163 О 5 10 15 20 i # 1 # j 1! 344555554321 21444555554321 3! 444666664321 4*556777776431 5 ! 556777776431 6 ! 5S6777776431 7 1556777776431 8! 556777776431 9 1444666664321 10 1333444443321 11 «222333332211 13 ! 14 ! 15 ! 16 ! Рис. 5.3. Результаты фильтрации. Алгоритм KIFILS, окно 5x5. Результат фильтрации тестового поля показан на рис. 5.4. Спад яркости на границах квадрата теперь симметричен, но детали изо- изображения, лежащие вблизи краев поля, дают «хвост» на противо- противоположном краю. Листинг 5.3. Функция KIFILT finclude <stdio.h> ¦include "IMAGE.H" /¦ ФАЙЛ — KIFILT.С ¦/ void KIFILT (lB&g«,Vi,Ij, Hp,Hq, IeOut) /* Т-схеиа */ SHOW lB&g«[], Mhit[j; int li.Ij.Mp.Hq; /¦ ф/ /¦ Фильтрация скользящим окном - на тор*. ¦/ /* Iaage " исходно* изображение */ /* IaOut - профильтрованное поле */ /¦ ... ¦/ { int register i,j; int p,q; long k, /* текущий элемент старого поля */ kc, /* элемент нового поля (куда пищей результат) */ kw, /* положение начала окна (на старой поле) */ kt; /* начало очередной строки окна (на старой поле)*/
164 Глава 5. Локальная фильтрация изображения long int в; int рв-(Мр+1)/2, qa»(Hq-H)/2; int vinsB"Mp*Mq; int sum; /* условный центр окна. */ /* сумма, элементов окна */ for (кс*НШ.(рв,яв), kv»O; if ( кс >» Vol > кс -¦ Vol; sub * 0; , kc++) for (kt«k?, i«l; i<«Hp; i**, kt ¦« Ij) for (k»kt, j»l; j<»Hq; j-н-, к-м-) < if ( к >» Vol ) к — Vol; sub +« (int) Iaag«[k]; } Ia0ut[kc] * (int) ( (float)sum / vinsa + 0.5 ); Приведенные выше картинки можно увидеть на экране ком- компьютера, если воспользоваться тестовой программой KIFI1, пока- показанной на листинге 5.4. Эта программа позволяет сформировать 10 15 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ! 1 ! 2 ! 2 ! 3 • 3 ! 3 ! 3 ! 3 ! 2 ! 2 ! l ! 1 ! 1 2 3 з 4 4 4 4 4 з 3 2 1 1 2 3 4 6 6 6 6 6 4 з 2 1 1 3 4 6 7 7 7 7 7 6 4 3 1 1 3 4 6 7 7 7 7 7 6 4 3 1 1 з 4 6 7 7 7 7 7 6 4 3 1 1 з 4 6 7 7 7 7 7 6 4 3 1 1 3 4 6 7 7 7 7 7 6 4 3 1 1 2 2 1 3 с 6 4 6 4 6 4 6 4 6 ' 4 : з : 2 : 1 1 1 1 * 2 I 3 I 3 L 3 I 3 I 3 * 2 * 2 2 1 L 1 L 1 1 . . . . 1 . . . , 1 . . . , 1 . . . . 1 . . . . 1 . . . , 1 . . . . 1 . . . . 1 . . . . 1 . . . . 1 . . . , . . . 1 , . . . 1 , . . . 1 . . . . 1 , . . . 1 , . . . 1 . . . . 1 . . . . 1 . . . . 1 . . . . 1 . . . . 1 Рис. 5.4, Результаты фильтрации. Алгоритм KIFILT, окно 5x5.
5.3. Базовый алгоритм локальной фильтрации 165 одно из тестовых полей, имеющихся в нашем распоряжении, про- просмотреть его на экране, выбрать размер окна, выбрать один из трех вышеописанных алгоритмов фильтрации, профильтровать поле и отобразить результат на экране. Для ввода размеров тестового поля и динамического распреде- распределения памяти программа вызывает функцию KIMEMO, для ввода размеров окна — функцию KIMMAT. Листинг 5.4. Тестовая программа KIFI1 finclude <stdio.h> finclude "IMAGE.H" /¦ ФАЙЛ — KIFI1.C ¦/ /¦ Фильтрация изображений */ /¦ скользящий окном */ void aainO { SHOW *lB&ge, «IaOut; int *i»16, Ij»20, Mi«3, Mj»3, lv«7; char old«'i\ End»'I'; int 8спеве*2; puts ("\n\n\tKIFIl - Фильтрация изображений скользящим окном."); puts ("\t\tЛинейные беэмасочные фильтры"); put» C"\t\tBecoBafl функция - тождественно равна единице"); do { if ( KIMEMO (felBage.tlaOut, *Ni,trj) ) KIMGAU (Ieaga, 14,1 j, fclv); puts(" Исходное изображение"); KIOTO (I«age, Mi,Hj); PAUSE. KIMMAT B500, tHi,tHj); /* Ввод размеров окна */ puts С1); puts ("\t Выберите схему перемещения окна:"); puts ("\t 1. Окно в пределах поля (оставляет пустые края)"); puts ("\t 2. Центр окна в пределах поля (без пустых краев)"); puts ("\t 3. Сканирование на торе"); KDVINT ("Ваш выбор?", *&спеяе,1,3); switch (scheae) { case 1: KIFILP (Iaage,Ni,Nj, Mi,Mj, IaOut); break; case 2: KIFILS (lBage,Hi,Hj, Mi,Mj, IeOut); break; case 3: KIFILT (lBage,Ii,Hj, Mi,Hj, IwOut); break; puts("Профильтрованное поле"); KIOTO (IaOut,Ni,Mj); PAUSE.
166 Глава 5. Локальная фильтрация изображения } while ( «KDVYES (" Закончить работу?",ftEnd) ); free (Iaage); free (IiOut); EID. Подпрограмма KIMEMO (листинг 5.5) служит для ввода разме- размеров обрабатываемого поля и динамического распределения памяти для хранения исходного и профильтрованного полей. Бели требу- требуемая память не может быть выделена, происходит останов. При первом обращении к этой подпрограмме память распределяется всегда, а при последующих — только при необходимости сфор- сформировать новое тестовое поле. В последнем случае сначала осво- освобождается ранее выделенная память. Подпрограмма возвращает 1, если память распределена, и 0, если память, не перераспределя- перераспределялась. Вызывающая программа анализирует, что вернула KIMEMO, и, если это не нуль, вызывает подпрограмму формирования тесто- тестового поля. Листинг 5.5. Функция КШБМО •include <stdio.h> •include "IMAGE.H" /* ФАЙЛ — KIMEMO.С */ int KIMEMO (IeageJeOut, Mi,Mj) SHOW **lBage, **IiOut; int *Ii,*Ij; /¦ ф/ /* Установка, н контроль раэиеров поля, */ /* распределение памяти под два одинаковых поля. */ /¦ Возвращает: */ /* 1 - если надо формировать новув картинку н */ /* О-в противном случае. */ /¦ ¦/ /¦ Hi, Ij - размеры полей */ { static char old-'i'; COUIT need; puts <"¦¦>; if ( old»»'i' || !KDVYES ("Картинку сохранить?", told) ) { if ( old !¦ »i» ) { puts("Освободим память"); free(*Iaage); free(*IaOut); > old * >Y\-
5.3. Базовый алгоритм локальной фильтрации 167 KDVIIT ("Высота поля, строк Mi", Ii, 0,512); KDVIIT ("Ширина поля, столбцов Nj", Nj, 0,512); KIMERR ("KIMEMO:", *Ni,*Nj); /* Контроль размеров ¦/ need ¦ *Ii * *Ij; puts ("Выделим память"); if (!(*Image*»rialloc(need))) { puts ("Нет места, для Image!"); STOP. } if (•(¦ImOutHealloc(need))) { putsO'HeT места для IeOut!");STOP. } return 1; > else return 0; Для ввода размеров окна используется функция КШМАТ (ли- (листинг 5.6), имеющая 3 параметра: максимально допустимое чи- число элементов окна, число строк и число столбцов окна. Послед- Последние два параметра служат для передачи значений в вызывающую программу, поэтому соответствующие переменные при обращении должны быть снабжены знаком амперсенда. Перед обращением к функции КШМАТ этим переменным должны быть присвоены начальные значения размеров окна. Начальные значения удобно задавать при описании переменных; эти значения будут предло- предложены программой при первом обращении к ней. Выбор размеров окна происходит в режиме диалога. Для вы- выбора квадратного окна достаточно указать одно число, которое и будет взято в качестве размера стороны окна. Для того чтобы задать прямоугольное окно, следует ввести 0, тогда появляются запросы на ввод размеров окна по вертикали и по горизонтали. Подпрограмма возвращает 1, если введен новый размер окна, и 0, если сохранен прежний размер. Листинг 5.6. Функция КШМАТ ¦include <stdio.h> finclude "IMAGE.H" /¦ ФАЙЛ — KIMMAT.С ¦/ int KIMMAT (Nv, Mi,Mj) int Nv; int *Mi,*Mj; /¦ ¦/ /* Выбор размеров окна */ /* Nv - максимально допустимое число элементов окна */ /¦ Mi,Mj - размеры окна ( Mi ¦ Mj <« Nv ) ¦/
168 Глава 5. Локальная фильтрация изображения /* Возвращает: 0 - если раянер окна сохраним ¦/ /¦ 1 - если введен новый размер окна */ /¦ ¦/ < static char bvnB>Y'; /* размеры окна сохранить */ printf (" Установлен размер окна %d x %d \n", *Mi,*Mj); if ( «KDVYESO'PasMep сохранить?", ftbvn) ) { static int ura*3; puts (M\n\tУкажите размер стороны для квадратного окна'1); KDVIMT ("О - задать прямоугольное окно", fcura, 0,Iv/2); if ( ига ) *Mi » *Mj ¦ ига; if ( ига «» О || ига*ига > Iv ) { KDVIMT ("Размер окна: - строк Hi", Mi, I, Iv); KDVIIT (••- столбцов Mjf\ Mj, 1, Iv / *Mi ); } return 1; } return 0; 5.4. Линейные фильтры Как следует из названия, отклик линейного фильтра линейным образом зависит от обрабатываемого изображения. Мы рассмо- рассмотрим фильтры, в которых для каждого положения апертуры осу- осуществляется поэлементное перемножение весовой функции на зна- значения соответствующих элементов изображения, суммирование произведений и нормировка полученной суммы. Введем необходимые обозначения. Пусть апертура имеет раз- размер Мр х Mq элементов; текущий элемент апертуры обозначим через (р,д), где р = 1, 2,... Мр — текущая строка; q = 1, 2,... Мд — текущий столбец. Определим способ, с помощью которого указывается положе- положение апертуры на изображении. Выделяется некоторая опорная точка апертуры (обычно это центр, реже — один из угловых эле- элементов). Теперь достаточно задать положение этой опорной точки в системе координат изображения, чтобы тем самым определить положение всей апертуры.
5.4. Линейные фильтры 169 Мы тоже возьмем за основу описанный способ. Упомянутую опорную точку будем называть условным центром апертуры; ко- координаты условного центра обозначим через (рт,Ят) (в системе координат апертуры!). Условный центр может (но не обязан) совпадать с настоящим, геометрическим центром апертуры. Вообще говоря, в качестве условного центра можно взять любую точку апертуры; более того, условный центр не обязан даже находиться внутри нее — можно, скажем, задать рт = 0, qm = 0. Мы, однако, определяем условный центр так, чтобы при нечетных размерах апертуры он совпадал с ее центральным пикселем: Рт = Чт = где квадратные скобки обозначают целую часть числа (функцию «антье»). Текущее положение условного центра на исходном изображе- изображении F обозначим через (t,i). Отклик фильтра присваивается той же точке (t,,;) нового, профильтрованного поля Q. Обозначим теперь через H(p>q) функцию окна. Массив Q выходного изображения формируется путем дискретной свертки входного поля F и функции окна #(р, q): Af, Q(U) = EE^-p™ +* i- «m + «)Я(р,«). E.1) Строго говоря, формула справедлива лишь при условии, что функция окна не выходит за пределы исходного изображения, т. е. выполняются условия, соответствующие Р-схеме движения окна: Рт <i< p 4m<j< Nj - Mq 4- gm, где Ni, Nj — размеры изображения. На случай 5- и Т-схем движения окна, когда условный центр перемещается по всему изображению и выполняются неравенства формула EЛ) распространяется с рядом оговорок, вытекающих из проведенного в разд. 5.2 обсуждения. Эти оговорки касаются способа расчета отклика фильтра в тех положениях окна, когда некоторые его элементы выходят за пределы изображения. Так, в
170 Глава 5. Локальная фильтрация изображения 5-схеме необходимо либо проводить суммирование только по тем элементам окна, которые находятся на изображении, либо доопре- доопределить изображение вне его границ. В Т-схеме надо программно «заворачивать» выходящие за пределы поля части окна на проти- противоположный край поля. Различные виды линейных фильтров отличаются своими весо- весовыми функциями и нормирующими коэффициентами. Обычно используются апертуры размером 3x3 элемента; уве- увеличение размеров апертуры существенно увеличивает объем вы- вычислений, в то время как качество обработки улучшается незна- незначительно [5, 29]. Впрочем, с ростом производительности процессо- процессоров ЭВМ вычислительные затраты все меньше лимитируют размер применяемых апертур, и в последнее время в употребление входят апертуры размером 5 х 5 и даже 7x7 элементов. Рассмотрим теперь ряд конкретных примеров линейных филь- фильтров. Приведем некоторые наиболее употребительные маски и по- покажем результаты фильтрации на примере тестового поля, содер- содержащего квадрат, линию и точку на нулевом фоне (рис. 5.5). Далее в пределах этой главы под тестовым полем будет пониматься имен- именно это изображение (если не оговорено иное). Одно из наиболее распространенных применений линейных фильтров — сглаживание шума. Для этого применяются весо- весовые функции следующего вида [5, стр. 332]: E.2) # = # = 1 1 9 1 1 То 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 E.3) При частотной интерпретации процессов фильтрации шумопо- давляющий фильтр является фильтром нижних частот. Филь- Фильтры, рассмотренные в предыдущем пункте, использовали весовую функцию вида E.2), задаваемую неявно. На рис. 5.6 и 5.7 показаны результаты фильтрации тестового поля с масками вида E.2) и E.3) соответственно. Забегая вперед, сообщим, что эти два поля и ряд последующих получены с помо- помощью подпрограммы фильтрации KIFOLS и тестовой программы KIFO2F, которые будут описаны в разд. 5.5. Для подчеркивания линий определенного направления исполь-
5.4. Л инейиые фильтры 171 О 5 10 15 20 • • • • • 1 ! 2 ! 3!.. 77777777 4!. .77777777 5!. .77777777 7.... 6!. .77777777 7!. .777 7 7777 8!. .77777777 9!. .77777777 10!..77777777 11 ! 12 ! 7 7 7 7.. 13 ! 14 ! 15 ! 16 ! Рис. 5.5. Тестовое поле. 0 5 10 15 20 .... ^^ ^ . 1 ! 2!. 1222222221 31.2355555532 4!. 2577777752... 111 5!. 2577777752... 111 6!. 2577777752... 111 7!. 2577777752 81.2577777752 91.2577777752 10 ! .2355555532 11 !.1222222221. .122221. 12! 122221. 13! 122221. 14 ! 15 ! 16 ! Рис. 5.6. Результаты фильтрации. Алгоритм KIFOLS, окно 3x3, маска вида E.2). ... ... ...
172 Глава 5. Локальная фильтрация изображения 10 15 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1122222211 1455555541 2577777752 2577777752 2577777752 2577777752 2577777752 2577777752 145555554 11222222 1 1 1 111 111 111 112 2 11 12 3 3 2 1 112 2 11 Рис. 5.7. Результаты фильтрации. Алгоритм KIFOLS, окно 3x3, маска вида E.3). зуются весовые а) Я = функции 1 16 1 2 1 2 4 2 вида 1 2 1 б) ТТ 1 16 2 1 2 1 4 1 2 1 2 E.4) Весовая функция E.4а) подчеркивает большими весами четы- рехсвязные элементы исходного изображения, т. е. горизонталь- горизонтальные и вертикальные линии, E.46) — диагональные линии (точнее, восьмисвязные элементы изображения, не являющиеся четырех- связными), Рез>|льтаты применения этих масок к тестовому полю показаны на рис. 5.8. Фильтры, подчеркивающие границы (в частотной интерпре- интерпретации — это высокочастотные фильтры), используют следующие три типовые весовые функции [5, стр. 334]: а) # = 0 1 0 _1 5 0 — 1 0 б) ТТ 1 1 X X -1 9 1 1 X X -1 -1 -1
5.4. Линейные фильтры 173 в) ТТ 1 -2 1 -2 5 -2 1 -2 1 E.5) Обратим внимание на то, что сумма весовых множителей здесь равна единице. Границы подчеркиваются независимо от их направления, что хорошо видно на рис. 5.9, где показаны результаты фильтрации тестового поля с масками вида E.5). Для выделения перепадов определенной ориентации использу- используются в зависимости от требуемого направления весовые функции, называемые курсовыми градиентными масками [5, стр. 502] ТТ в) Я = д) ТТ ж) «север» 1 1 -1 1 -2 -1 «восток» -1 -1 — 1 1 -2 1 «юг» — 1 1 1 —1 —2 1 «запад» 1 1 1 1 •—2 1 1 1 -1 1 1 1 —1 1 1 -1 -1 -1 о^ «северо-восток» тт _ 1 -1 -1 1 -2 -1 1 1 1 г) «юго-восток» и- е I # = — 1 —1 1 -1 -2 1 1 1 1 «юго-запад» 1 1 1 -1 -2 1 -1 -1 1 з) «северо-запад» тт _ 1 1 1 1 -2 -1 1 -1 _1 E.6) Название курса говорит о направлении перепада яркости, вы- вызывающего максимальный отклик фильтра. Результаты фильтрации показаны на рис. 5.10.
174 Глава 5. Локальная фильтрация изображения ! . 1 ! . 2 ! . 2 ! . 2 ! . 2 ! . 2 ! . 2 ! . 1 • • • 1 4 5 5 5 s 5 5 4 1 2 5 7 7 7 7 7 7 5 2 2 5 7 7 7 7 7 7 5 2 2 5 7 7 7 7 7 7 5 2 2 5 7 7 7 7 7 7 5 2 2 5 7 7 7 7 7 7 5 2 2 5 7 7 7 7 7 7 5 2 1 1 4 5 5 5 5 5 s 4 1 *•¦"¦ 1 . 2 . 9 1 • . . 1 . . 1 . 1 3 . . 1 1 2 1 2 4 2 1 2 4 2 i a a a 1 a a 3 1 a 1 a a 0 5 10 15 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Рис* 5.8а. Результаты фильтрации. Алгоритм KIFOLS, окно 3x3, маска вида E.4а). 0 5 10 15 20 1 ! 2!. 1122222211 3!. 1455555541 4!. 2577777752... 1.1... 5! .2577777752. . . .2. . . . 6!. 2577777752... 1.1... 78.2577777752 8!. 2577777752 9!. 2577777752 10 !. 1455555541 11!.1122222211.. 112211. 12 ! 2 3 3 2.. 13! 112211. 14 ! 15 ! 16 ! Рис. 5.86. То же, маска вида E.46).
5.4. Линейные фильтры 175 10 15 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 е е е е е е 1 е 7 7 7 7 7 7 е е 7 7 7 7 7 7 е е 7 7 7 7 7 7 е е 7 7 7 7 7 7 е е 7 7 7 7 7 7 е е 7 7 7 7 7 7 е 1 е е е е е е 1 Z . 8 1 1 8 . . Рис. 5.9а. Результаты фильтрации. Алгоритм KIFOLS, окно 3x3, маска вида E.5а). 0 5 10 15 20 i 1 1 • • • • • 1 ! 2 ! 3!..G888888G 4!. .87777778 5!. .87777778 \.... 6!. .87777778 7!. .87777778 8!. .87777778 9!. .87777778 10!. .G888SSSG 11 ! 12 ! U N N U . . 13 ! 14 ! 15 ! 16 ! Рис. 5.96. То лее, маска вида E.56).
176 Глава 5. Локальная фильтрация изображения 10 15 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 7 7 .в777777в. .77777777. .77777777. .77777777. .77777777. .77777777. .77777777. . е777777в. 7 7 7 f..f....f. ....1771.. 7 7 Рис. 5.9в. То же, маска вида E.5в). Для выделения перепадов без указания их ориентации исполь- используются следующие три вида весовых функций (операторы Лапла- Лапласа) [5, стр. 503]: а) б) ТТ 0 1 0 — 1 4 -1 в) Я ( ) -1 0 •мм» 1 _2 1 Я -2 4 —2 -1 — 1 — 1 1 —2 ¦ L • -1 8 -1 -1 -1 -1 E.7) Вопрос для наблюдательных: чем эти маски отличаются от ма- масок вида E.5)? И вопрос для сообразительных: как это отличие скажется на результатах фильтрации? Проверьте себя — результаты фильтрации приведены на рис. 5.11. Сравните их с рис. 5.9. Не правда ли, недостача единички в центре маски разительно меняет картинку? Весовые функции E.6) и E.7) позволяют осуществлять опера- операцию двумерного дифференцирования. В [29] отмечается, что ве- весовые функции, используемые для дифференцирования, должны обладать тем свойством, что сумма их элементов равна нулю.
5.4. Линейные фильтры 10 15 20 1 2 3 4 Б 6 7 8 9 10 11 12 13 14 15 16 .7 7 7 7 7 7 .7 7. ..777 • 7 ........ 7 .7 7 .7 7 .6711111176 .7б111111б7 7 .... 7 7 е 1 1 в 7 Рис. 5.10а. Результаты фильтрации. Алгоритм KIFOLS, окно 3x3, маска вида E.6а). 0 5 10 15 20 2 !. 7 3 ! . е 4 ! . 17... 7.... 5!. 17 7.... 6!. 17 777.. 7 1.17 81.17 9!. 17 10 К в 7 7 7 7 7 7 7 11! .7б111111б7. .7 12 ! . . . . 7 13!. 7е11е7 14 !. 15 ! 16 ! Рис. 5.106. То лее, маска вида E.66).
178 Глава 5. Локальная фильтрация изображения 10 15 20 1 ! 2!.7е777777 3! .6? 4!. 11 77. 5!. 11 7.. 6!. 11 77. 71.11 81.11 9!. 11 10 ! . е 7 11 !. 7 в 7 7 7 7 7 7 .... 7 в 7 7 12 ! 7 ... 13 ! 7 в 7 7 14 ! 15 ! 16 ! Рис. 5.10в. То лее, маска вида E.6в). 10 15 20 1 ! 2!.7е111111е7 3!. «7777777 4!. 17 777.. 5!. 17 7.... 6!. 17 7.... 7!. 17 8!. 17 9!. 17 10 ! . е 11!. 7 7е11е7 12 ! 7 13 ! 7 14 ! 15 ! 16 ! Рис. 5.10г. То же, маска вида E.6г).
5.4. Линейные фильтры 179 О 5 10 15 20 . . • — —» -¦ j 1 ! 2!.7е111111е7 3 ! .t71111117t 4!. 7 7. ..777... э!.7. ...... .7. ..7.7... 6 ! . 7 7 7!. 7 7 8!. 7 7 9!. 7 7 10 ! 11! 7е11е7. 12 ! 7 .... 7 . 13 ! 14 ! 15 ! 16 ! Рис. 5.10д. То лее, маска вида E.6д). 0 5 10 15 20 1 ! 2!.7е111111е7 3!.. .7777777е 4! 71. ..777.. 5! 71 7.. 6 ! 7 1 7 . . 7! 71 8! 71 9! 71 10 ! е 11 ! 7 . . 7 е 1 1 е 7 12 ! 7 13 ! 7 14 ! 15 ! 16 ! Рис. 5.10е. То нее, маска вида E.бе).
180 Глава 5. Локальная фильтрация изображения 10 15* 20 1 ! 2!...777777е7 3! 7 е 4 ! 1 1 .... 7 7 .. 5! XI 7.. 6! 11. ...77.. 7! 11 8! 11 9! 11 10 ! 7 в 11!...777777в7....77в7 12 ! 7 13 ! 7 7 в 7 14 ! 15 ! 16 ! Рис. 5. Юж. То же, маска вида E.6ж). 0 5 10 15 20 | тттттттттттттттттт штштштшжштштштштшт | mm mm mm mm mm mm mm тат шт а» тттт mm mm тш mm mm mm | • • » • • 1 ! 2 ! 7 3 ! e 4! 71 7... 5! 71 7... 6! 71. ..777... 7! 71 8! 71 9! 71 10!.. .7777777e 11 ! . 7 • 1 1 1 1 1 1 • 7 7. 12 ! 7 . 13! . . 7 e 1 1 e 7 . 14 ! 15 ! 16 ! Рис. 5.Юз. То лее, маска вида E.6з).
5.4. Линейные фильтры 181 О 5 10 15 20 i_________ _—_.———._—,_ i«_-.—.————_ _—————_—_ I : • •^^ • . 1 « 2 ! 3!..е777777е 4!. .7 7 *)...'......•....•&.... 6!. .7 7 7!. .7 7 8!. .7 7 9!. .7 7. 10 !.. в 7 7 7 7 7 7 в 11 ! 12 ! 1 е е 1 . . 13 Г 14 ! 15 ! 16 ! Рис. 5.11а. Результаты фильтрации. Алгоритм KIFOLS, окно 3x3, маска вида E.7а). 0 5 10 15 20 i 1 1 • • • • • 1 ! 2 ! 3 ! . . zllllllz 4 ! . . 1 1 5!..1 1 U.... 6!..1 1 7 ! . . 1 1 8 «... 1 1 9!..1 1 10!. .zllllllz 11 ! 12 ! N G G М . . 13 ! 14 ! 15 ! 16 ! Рис. 5.116. То лее, маска вида E.76).
. 7 . . . 7 182 Глава 5. Локальная фильтрация изображения О 5 10 15 20 . • . • » 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 7.7... . s . • . . 7.7... . . 7 7 • • •*..• .... ...о. «о*. 7 7 Рис. б.Ив. То же, маска вида E.7в). 5.5. Программы линейных фильтров На листингах 5.7-5.9 приведены 3 подпрограммы линейных филь- фильтров, KIFOLP, KIFOLS и KIFOLT, соответствующие трем схемам обработки краев. Подпрограммы имеют одинаковый набор фор- формальных параметров: исходное поле, его размеры, весовая функ- функция, размеры апертуры и выходное поле; всего 7 параметров. Их внутреннее устройство аналогично подпрограммам, описан- описанным в разд. 5.3; все, сказанное там, остается справедливым. Име- Имеющиеся отличия связаны с присутствием явно заданной маски и обусловлены необходимостью строго синхронизировать обход весо- весовой функции в окне с обходом участка поля, покрываемого этим окном. Проще всего синхронизация обеспечивается в подпрограммах KIFOLP и KIFOLT, где окно обходится всегда целиком. Доста- Достаточно изменять одномерный индекс массива window, содержаще- содержащего весовую функцию, от 0 с шагом 1, чтобы получить требуемую синхронизацию. В KIFOLS задача усложняется тем, что окно не всегда просма- просматривается целиком. Поэтому приходится организовывать обход весовой функции тем же способом, по которому обходится участок поля, накрываемый окном, т. е. находить одномерные координаты
5.5. Программы линейных фильтров 183 первых элементов строк и давать им единичные приращения для продвижения по строке. Отметим один тонкий момент, имеющий отношение ко всем трем подпрограммам. Ответьте, пожалуйста, на вопрос: что де- делать с откликом фильтра, если этот отклик отрицательный? Си- Ситуация вполне возможна: коль скоро существуют маски с отри- отрицательными весовыми множителями, будут появляться и отрица- отрицательные отклики. Итак, что с ними делать? Присвоить отрица- отрицательное значение элементу выходного поля мы не вправе, так как условились, что изображения состоят из неотрицательных элемен- элементов. Мы видим только два простых решения: либо заменить отри- отрицательный отклик нулем, либо взять его по абсолютной величи- величине. Но оба решения нелинейны! Эксперимент показал, что первое решение дает лучшие результаты, поэтому в наших программах реализован именно этот вариант. Так что, строго говоря, описываемые фильтры линейны только для масок с неотрицательными элементами. Листинг 5.7. Функция KIFOLP finclude <8tdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIFOLP.С ¦/ void KIFOLP (Image,Ii,Ij, window,Mp,Mq, ImOut) SHOW Image [] , ImOut[]; char window [] ; int /* Фильтрация скользящий окном - на плоскости */ /* Перемещение окно. - по строкам. */ /* Окно НЕ M0IET выходить за пределы поля, */ /* поэтому края профильтрованного поля остается пустыми! */ /* I*&ge ~ исходное изображение */ /* Mi, Mj - размеры исходного и профильтрованного полей */ /* window - весовая функция окна */ /* Мр, Mq - размеры окна */ /* ImOut - профильтрованное поле (сначала очищается) */ /* Внимание! Края этого поля не обрабатывается! */ /ф ф/ /* Результат обработки записывается в тот элемент */ /* поля ImOut, который накрывает точка окна (pm,qm). */ /* Это задано оператором (ko»MARK.(il+pm-l,qm).
184 Глава 5. Локальная фильтрация изображения < int register i,j; int r; COUIT k, ко; int il,jl, /¦ (il,jl) - левый верхний угол окна */ i2,j2; /+ (i2,j2) - правый нжжннй угол окна ¦/ int p»«(Mp+l)/2, qa«(Mq*l)/2; /¦ условный центр окна. ¦/ int vinea'O; /¦ сумма элементов окна */ int sub; /* очистка - для нерекурсивного фильтра */ if ( Iaage !¦ IaOut > { for (k«Ii*Ij; k>0; ) IaOut[--k] « MODATA; } г ¦ Hp*Hq; while ( г > 0 ) vin»B ¦¦ window[—r]; if ( vines ¦* 0 ) vines * 1; for (il«l,i2«Mp; i2<«Ii; for (ko«MARK_(il*pB-l,qB), j sub « 0; г ¦ 0; for (i«il; i<«i2; i for (k«MARK.(itj sub +« ( (int) Iaagetk] * (int) window[r] ); sub * (int)( (float)sue / vinsa 4 0.5 ); I«0ut[ko] ¦ (sub>0) ? sub : 0 ; Листинг 5.8. Функция KIFOLS finclude <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIFOLS.С ¦/ void KIFOLS (lBage,Ki,lj, window,Mp,Mq, XaOut) SHOW IaageD, IaOutG; char vindov [] ; int li,lj,Mp,Mq; /¦ Фильтрация скользяишм окном - на плоскости */ /¦ Центр окна пробегает все поле, перемещаясь построчно. */ /* Окно KOIET выходить за пределы поля, */ /* но обрабатывается только часть окна, лежащая на поле. */ /* Iaage - исходное изображение */ /* vindov - весовая функция окна */ /* IaOut - профильтрованное поле */ /* __ _ _ _ .____ .__ ___ _ _ _ ^ -. ___ ^/ { int register p,q; /¦ индексы окна (строка, столбец) ¦/
5.5. Программы линейных фильтров 185 int pi,p2, ql,q2; int zon; int r; int re; COUIT кя; int io,jo; COUIT ко; COUIT k; int pa«(Mp+l)/2, qe«< int vin*B,vinso»Q; int sue; /¦ /¦ /¦ /¦ /¦ /¦ /¦ /• [Mq* границы ианенеиия индексов окна ¦/ индикатор внутренней воин поля ¦/ текущий элемент окна ¦/ начало текущей строки окна ¦/ то же, в координатах поля ¦/ текущее положение центра окна */ текущее положение центра окна */ текущий »леиеит поля */ 2; /* условный центр окна */ /* сукна >леиеитов окна */ г ¦ Hp*Hq; while ( г > 0 ) vinzo ¦¦ window [—г]; if ( vinzo «¦ 0 ) vinzo • 1; for (ko«0,io«l; io<«Ii; pi p2 zon for 1+pe-io; if ( pi < 1 ) li-fpa-io; if ( p2 >Hp ) (pl««l kk p2»«Mp); pi p2 1; Hp; ql ¦ l+qB-jo; if ( ql < 1 ) ql ¦ 1; q2 « Ij*qB-jo; if ( q2 >Nq > q2 « Mq; if (zon kk ql**l kk q2**Hq) vines • vinzo; else { /* вяенняя зона: веревычисляек vineB vinsB ш 0; for (rs*(pl-i)*Mp*ql-lf p«pl; for (г»гя, q«ql; q<«q2; q**t vineB +¦ window[r]; if ( vines ¦» 0 ) vines * 1; sub * 0; kg for for (k«kef sub ¦¦ ( (int) sub * (int)( (flo&t)atiB / vinea ¦ 0.5 ); InOutCko] * (sus>0) ? sun : 0 ; ¦/ l» p*pi; p<«p2; ; q<«q2; q-и-, e[k] ¦ (int) vindov[r] ); Листинг 5.9. Функция KIFOLT finclude <3tdic.h> •include "IMAGE.H" /¦ ФАЙЛ — KIFOLT.С ¦/
186 Глава 5. Локальная фильтрация изображения void KIFOLT (Image,Ii,Ij, vindov,Mp,Mq, ImOut) SHOW Imag«[], ImOut[]; char vindov [] ; int /* Фильтрация скользящий окном - на торе */ /* Image - исходное изображение */ /* vindov - весовая функция окна */ /* ImOut - профильтрованное поле */ /Ф ф/ { int register i,j; /¦ текущие элементы массивов ¦/ int г; /* текущий элемент окна */ long k, /* текущий элемент старого поля */ кс, /* элемент нового поля (куда пишем результат) */ kv, /* положение начала окна (на старой поле) */ kt; /* начало очередной строки окна (на старой поле)*/ long Vol»Ii*Ij; int pm*(Mp+l)/2, qm*(Mq+l)/2; /* условный центр окна */ int vinsm*0; /* сунна элементов окна */ int sum; г * Mp*Hq; vhile ( г > 0 ) vinsm ¦* vindov[—г]; if ( vinsm «« 0 ) vinsm * i; for (kc«MARK.(pm,qm), kv«0; kv<«Vol; kv*+, kc+O { if ( kc >« Vol ) kc -« Vol; sum * 0; for (kt«kv, r«0, i«l; i<«Mp; i++, kt ¦« Ij) for (k«kt, j«l; if ( k >« Vol ) k -« Vol; sum 4« ( (int) Image[k] * (int) window[r] ); } sum * (int)( (float)sum / vinsm + 0.S ); InOut[kc] * (sum>0) ? sum : 0 ; ^ Тестовая программа KIFO2F (листинг 5.10) предназначена для проверки трех вышеописанных функций. Программа позволяет циклически повторять опыты по фильтрации; каждый из опытов может включать формирование исходного изображения, создание маски, выбор подпрограммы фильтрации и обработку изображе- изображения. В первом (после запуска программы) цикле выполняются
5.5. Программы линейных фильтров 187 все перечисленные этапы без исключения; в последующих — мо- могут быть опущены этапы формирования исходного изображения и создания маски. Программа позволяет не только наблюдать результаты филь- фильтрации на экране, но и сохранять их в дисковом файле с фик- фиксированным именем KIFO2F.DAT (Вы, конечно, можете добавить в программу несколько операторов, чтобы вводить имя файла с терминала). Непосредственно после своего запуска программа за- запрашивает, надо ли сохранять полученные результаты в файле. При отрицательном ответе файл не открывается и вопрос счита- считается исчерпанным до следующего запуска программы. При по- положительном ответе программа открывает файл (но не спешит в него что-либо записывать) и запрашивает номер, с которого надо начать нумерацию протоколов (по умолчанию — с единицы). По- Полученные в каждом опыте результаты выводятся в файл только при положительном ответе на соответствующий запрос програм- программы. Такая организация записи позволяет проводить опыты без протоколирования до тех пор, пока не будет достигнут желаемый эффект фильтрации, и сохранить нужный результат, как только он будет получен. Нумеруются не проводимые опыты, а имен- именно протоколы. Протокол опыта содержит использованную маску, профильтрованное изображение и подпись, в которой указан по- порядковый номер протокола, имя подпрограммы фильтра и размер окна. Листинг 5.10. Тестовая программа KIF02F •include <8tdio.h> •include "IMAGE.Ни /¦ ФАЙЛ — KIF02F.C ¦/ •include "KDV.H" /* Фильтр изображений */ •define LENWIH 50 /* Какс. число элементов окна */ char cfi * >N\ сш» « 'М>; char *fit * "KIF02F.DAT111; /¦ Имя файла вывода ¦/ FILE *Fout, *fopen(); int NumExp«l; char «algorithm[4] » { "", "KIFOLP", "KIFOLS", "KIFOLT" }; void nain() < static char window [ LENWIN ]; SHOW «Image, *IiOut; int Ii«16, Nj-20;
188 Глава 5. Локальная фильтрация изображения int Mi«3, Mj«3; int j, lv«7; char uap-'i', End*'M int numal*2; puts ("\n\n\tKIF02F - Фильтрация изображений скользящий окном"); puts ("\t\t Линейные фильтры с произвольной маской"); if ( KDVYESC"Сохранять ля результаты в файле?", fccfi) ) { if ( cfi «« >у> ) cfi « >Y>; Fout ¦ fopen (fit,"»11); if ( Fout ) printf ("XtBuBOA результатов в файл %s\n", fit); else { printf ("Файл Xs не открывается!'1, fit); STOP. > do { if ( KIHEHO (*Image,tImOut, cNi,feNj) ) { if ( KIMGAU (Image, Mi.Mj, klv) «« 3) { if (Ii>8 fcfc Hj>10) { Image[5*Hj-5] I[(Mi4) gj lv; Image[(Mi-4)*Hj-3] « lv; Image[(Wi-4)*Hj-4] « lv; Image[(Ii-4)*Wj-5] « lv; ImageC(Wi-4)*Hj-6] « lv; > puts(" Исходное изображение"); KIQTC(Image,Wi,Wj); PAUSE, if (cfi»«>Y> cc KDVYES(amicaTb это поле в файл?11, жсюи) ) KIOFPG (Fout,0,Image, Hi,Hj, l,Hi, l,Hj, 2,79,60); if (uap««'i* || «KDVYES (" Функции окна сохранить?", fcuap) > if (uap—'iO uap»'H»; KINNFU (vindov, LENWIN, puts ("\n\t Выберите подпрограмму фильтрации:"); puts ("\t 1. KIFOLP: Все окно в пределах поля"); puts ("\t 2. KIFOLS: Центр окна в пределах поля"); puts ("\t 3. KIFOLT: Фильтрация на торе"); KDVINT ("Ваш выбор?", fcnumal,l,3); switch (numal) { case 1: KIFOLP (Image,Vi,Nj, window,Mi,Mj, ImOut); break; case 2: KIFOLS (Image,Mi,Mj, window,Hi,Nj, ImOut); break; case 3: KIFOLT (Image,Mi,Mj, window,Hi,Nj, ImOut); break; > puts ("Профильтрованное поле"); KIOTO (ImOut, Ni,Mj); PAUSE.
5.6. Программы формирования масок 189 if (cii»"Y' fcfc KDVYESC"Записать результаты в файл?", fccmm) ) { KIMMDW (Fout,8,window,Mi,Mj, 3, 1); KIOFPG (Fout,0,ImOut, Ii,Ij, l,Ii, l,Ij, 2,79,60); fprintf(Fout,"\nPHC. %d. У.».\пАлгоритм Х», окно %dx%d.\n", NumExp++,"Результаты фильтрации", algorithm[numal],Mi,Hj); } while ( «KDVYES (" Закончить работу?",*End) ); free (Image); free (ImOut); if ( cfi *» »Y» ) { fprintf(Fout,M\n*\nM); fclose(Fout); } EMD. 5.6. Программы формирования масок Маски (весовые функции) по своей структуре не отличаются от изображений. И те и другие представляют собой прямоугольные таблицы (матрицы) целых чисел, но в программах размещаются в одномерных массивах. Маски имеют одно важное отличие от изображений: если изображения составлены из неотрицательных элементов, то элементами масок могут быть как отрицательные, так и положительные числа. В программах маски хранятся в мас- массивах типа char, поэтому элементы масок принимают значения в диапазоне от —128 до 127. В нашем распоряжении имеются следующие подпрограммы для создания и управления масками (в скобках указан номер ли- листинга): — выбор размеров окна, — вывод маски на экран, — вывод маски в файл, — создание бинарной маски, — выбор стандартной маски 3x3, — выбор размеров и создание маски, — диалоговая коррекция маски, — прямая по алгоритму Брезенхема, — создание маски в виде рамки. KIMMAT KIMMD KIMMDW KIMMFB KIMMFO KIMMFU KIMMIN KIMMLI KIMMSM E.6) E.11) E.11) E.12) E.13) E.13) E.14) E.12) C.9) Прототипы этих подпрограмм находятся в файле KIMM.H (ли- (листинг 5.15). Подпрограмма KIMMAT была рассмотрена нами в разд. 5.3, подпрограмма KIMMSM — в разд. 3.8. Рассмотрим оставшиеся.
190 Глава 5. Локальная фильтрация изображения Подпрограмма KIMMDW предназначена для числового вывода маски в заданный файл. Она устроена аналогично подпрограм- подпрограмме вывода изображений KIMGDW, но в отличие от нее выводит не фрагмент, а всю маску целиком. Параметры подпрограммы позволяют устанавливать величину левого поля, ширину колонок, задавать или отменять оцифровку осей. Подпрограмма KIMMD предназначена для числового вывода маски на экран. Подпрограмма сводится к вызову KIMMDW со следующими параметрами: левое поле равно нулю, 3 знакоместа, на колонку, оцифровка осей включена. Листинг 5.11. Функции KIMMDW и KIMMD •include <8tdio.h> ¦include "IMAGE.H" /* ФАЙЛ — KIMMDW.С */ void KIMMDW (Fout,left,Mask, li,lj, wl,X0Y) FILE *Fout; char Mask [] ; int left,Ii,Ij, wl,X0Y; /¦ ф/ /* Числовой вывод каски (аналогична. KIOFDW) */ /* XOY - оцифровка осей: 1 - да, 0 - нет */ /„ Ч { int i,j; COUIT k,kO; KIMERR <"KIMMDW:",li,tfj); /* Контроль размеров поля */ if ( left<0 ) left«0; if ( wl<2 ) wl«2; if ( wl>8 ) ?l«8; if (XOY) KIOFAX(Fout,left,l,Ij,wl); /¦ Оцифровка гориэ.оси ¦/ for (i«l, k0«0; i<«Ii; i*+, k<H«Ij) { if (XOY) fprintf (Fout,"X*d !иДеП*4Д); else fprintf (Fout,"%*sn, left,1); for (k-kO, j-Mj; j>0; j—) fprintf (Fout,nX«d", ?l,Mask[k**3); fprintf (Fout,n\nn); >¦ if (XOY) fprintf (Fout,"\nM); void KIMMD (Mask, li,lj) char Mask []; int Ki,Mj; /¦ Числовой вывод маски на экран */ XINNDV (stdout, 0, Mask, Ri,Wj, 3,1);
5.6. Программы формирования масок 191 Под] программа KIMMFB позволяет формировать различные би- бинарные маски, состоящие из элементов 0 и 1. Размер маски зада- задается параметрами подпрограммы. Можно выбрать одну из пя- пяти разновидностей масок: константа, рамка, крест, косой крест и прямой утолщенный крест. Последнее — только для масок разме- размером более 4x4 элемента. Выбранную маску можно инвертировать, т. е. заменить все нули на единицы и наоборот. Подпрограмма KIMMFB использует для формирования масок подпрограмму KIMMLI, строящую прямую на дискретном поле. Эта подпрограмма находится в том же файле. Подпрограмма KIMMLI аналогична подпрограмме KIGLIN и отличается от нее лишь типом обрабатываемого поля: вместо типа SHOW использу- используется поле типа char. Листинг 5.12. Функции KIMMFB и KIMLI finclude <8tdio.h> «include "IMAGE.H" «include "KDV.H" /¦ ФАЙЛ — KIMMFB.С ¦/ «define LV 1 /* уровень ненулевых элементов маски */ void KIMMFB (window, Ni,Nj) char window [] ; int Ni,Hj; /* Создание бинарной маски (выбор в режиме диалога) window - массив под маску Ni,Nj - размеры маски (окна) register int j; int i,k,kO, ww; int ie«(Mi-H)/2,jii static char binv^' static int ufw«l; static int bord«l; /* условный центр окна */ /* инверсия маски */ /* выбор вида маски */ /* ширина рамки */ ¦/ */ if ( Mi<3 Mj<3 ) { j«Ii*Ij; while(j) window[ —j ] « LV; return; > ww « Mi>4 Nj>4; printf ("\n\t Выберите бинарнув маску \п"); printf ("\t 1. Константа\п"); printf ("\t 2. Рамка\пм);
192 Глава 5. Локальная фильтрация изображения printf ("\t 3. Крест\п"); printf ("\t 4. Косой крест\п"); if ( ww ) printf ("\t 8. Толстый крест\п"); KDVI1T ("But выбор?", fcufv, 1, (vv>?5:4>; j»Ii*Mj; while(j) window[ —j ] ¦ 0; switch (ufv) { ce.ee 1: j»Ii*Ij; while(j> window[ — j ] ¦ LV; break; 2: KDVIXT ("Ширина, рамкн", tbord, 0. ((li<lj)?Ii:lj)/2); /¦ kO»bord*Ij*bord - то же, что HAlUK_(bordn,bord*l) ¦/ for (kO*bord*fj4bord, i-Ii-2*bord; i>0; i~~, k<H«Ij) for (k»kO, j»Ij-2*berd; j>0; j—, &¦*+) windo«[k] » LV; break; case 3: KIHMLI (wird^w, Ii,Hj, 1»jb» KIHHLI (vindov, Ii,»J, ia,l, ia,»j, IV); break; case 4" KIHHLI (window, Ii,Ij, i,l, litlj, LV); KIHMLI (window, Ii,Bj, l,Ij, Ii,l, LV); break; case 5: w » (Ij<5) ? 0 : 1; for (j ¦ -mr; j<*ww; KIMMLI (window, vw » (Ii<5) ? 0 : 1; for (j ¦ "V9; j<«ww; j**) KIHHLI (window, Ii,Ij, i»+j,l, i»+j,Sj, LV); break; } if ( KDVYES ("Инверсия?", fcbinr) ) for (j»Ii*Ij-i; j>*0; j—) window[j] ¦ !window[j]; > «define FT0E(x,I) if ((x*(x-l)XW) <» 0) x ¦» 1 static void KIMMLI (Iaage, Ii,Ij, il,jl, i2,j2, L) char Image[]; int Ii,Hj; int il,jl, i2,j2, L; /* Проведение прямой на дискретном поле. */ /* Алгоритм Бреэенхена. */ /* ВНИМАНИЕ: поле свернуто в торожд */ /* il.jl - начальная точка */ /* i2,j2 - конечная точка */ /* L - уровень, которым заполняется пряная */ { int delt.i, delt.j; int Dx»Dy, eps, ex,ey, stx,sty;
5.6. Программы формирования масок 193 int t; FTOR (i2,Ii); FTOR delt.i » i2-il; delt.j * j2-jl; Dx « abe(delt.i); Dy » abe(delt.j); etx • ( d«lt.i>0 ) ? 1 : -1; /* в&гж по коорджж&т&и */ ety » ( d*lt.j>0 ) ? 1 : «i; if (Dx >* Dy) { «p» « 2*Dy - Dx; ; ex ¦ 2*Dy: for <t«0; t<»Dx; t-^) { Image [KARK.Cil,ji)] » L; il ¦¦ etx; if <«ps>0) { jl +* sty; eps ¦» «y; } «lee epe ¦•¦» «x; > } else { eps ш 2*Dx - Dy, ey ¦ 2*Dx - 2*Dy; ex ¦ 2*Dx; for (t«0; t<^Dy; t-м-) { Isage [MARK.Cil,jl)] » L; ji +« ety; if (eps>0) { il +» stx; ере +Ж «у; } else epe +* ex; } Подпрограмма KIMMFO позволяет выбирать стандартные мас- маски размером 3x3 элемента видов E.2)-E.7). Подпрограмма пред- предлагает на выбор 13 масок, отображаемых на экране. Подпрограмма KIMMFU, показанная на том же листинге, объ- объединяет все операции по формированию маски. Для ввода разме- размеров вызывается подпрограмма KIMMAT, для создания масок — KIMMFO или KIMMFU (по выбору). Сформированная маска ото- отображается на экране подпрограммой KIMMD. При необходимости может быть проведена поэлементная коррекция, что позволяет со- создать любую маску. Листинг 5.13. Функции KIMMFO и KIMFU tinelude <stdio.h> iinclude "IMAGE.H" /¦ ФАЙЛ — KIMMFO.С ¦/ •include "KDV.H"
194 Глава 5. Локальйая фильтрация изображения Toid XIMMF0 (Нтея) char &>•»[9]; /* Выбор стандартной маскл 3x3 */ int г, row,гв, static int Iof; static char Ves [ 18*9 ] ¦ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 4, 2, 1, 2, 1, 2, 1, 2, 1, 4, 1, 2, 1, 2, 0,-1, 0, -1, 5,-1, 0,-1, 0, -1,-1,-1, -1, 9,-1, -1,-1,-1, 1,-2, 1, -2, 5,-2, 1,-2, 1, 1, 1, 1, 1,-2, 1, -1,-1,-1, 1, 1, 1, -1,-2, 1, -1,-1, 1, -1, 1, 1, -1,-2, 1, -1, 1, 1, -1,-1, 1, -1,-2, 1, 1, 1, 1, -1,-1,-1, 1,-2, 1, 1, 1, 1, 1,-1,-1, 1,-2,-1, 1, 1, 1, 1, 1,-1, 1,-2,-1, 1, 1,-1, 1, 1, 1, 1,-2,-1, 1,-1,-1, /¦ /¦ w /¦ /¦ /* /¦ /¦ /¦ /¦ /¦ /¦ /¦ /¦ /¦ /¦ /¦ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 НЧ-фнльтр сглаживание шума ВЧ-фильтры курсовые маски: север северо-восток восток ¦го-восток ¦г ¦го-запад запад северо-запад ¦/ ¦/ W ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ 0,-1, 0, -1, 4,-1, 0,-1, 0, /* 15 операторы Лапласа */ -1,-1,-1, -1, 8,-1, -1,-1,-1, /¦ 16 ¦/ 1,-2, 1, -2, 4,-2, 1,-2, 1}; /f 17 ¦/ printf ("\tBn6epMTe маску нз следующего набора\п\п"); for (ro?*0; ro?<*12; ro?**6) { /* вывод масок на экран */ гв * го?*5; for <г«го?; r<»ra; г**) printf <"i%-12d",r);pute <""); for (line*0; line<3; li { for (r»9*ro?*3*linet el*0; el<6; si**, r**9) printf (" %3d%3dX3d ••, Ves[r],Vee[r*l],Ves[r*2]); puts<""); puts (""); } KDVIIT ("Номер выбранной маски", fclof, 0,17); for (r*0; r<9; r**) HveeCr] * Vee[9*Iof*r]; > void KIHHFU (window, Iw, Hi,Hj)
5.6. Программы формирования масок 195 /¦ /¦ /¦ char window [] ; int Iv; int *Mi,*Nj; Выбор размеров н создание маски с диалоговый управлением ¦/ ¦/ / /¦ window - массив для хранения маски /¦ Iw - число элементов в нем /¦ Mi.Mj - размеры каски ( Mi ¦ Mj <- Iw ) int new; static char b?f»'Y'; static char ЬЗЗ-'Y'; static char bcr»'!'; static char bye»'Y'; /¦ обновить маску /* выбор масок 3x3 /* поэлементная коррекция /* подходящая маска ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ ¦/ Lbeg: new - KIHHAT (Iw, Hi,Hj); if ( new ) KIHHD (window, *Hi,*Hj); if (new П KDVYES ("Создать новуя маску?", cbvf) ) if (*Mi««3 kk *Hj««3 kk KDVYES("Специальные маски ЗхЗ?",сЬЗЗ)) KIMMFO(window); else KIMNFB (window, *Hi,«Mj); KIMMD (window, *Hi,*Hj); if ( KDVYES (" Поэлементная коррекция требуется?", ftbcr) ) KIHHII (" Коррекция маски", window, *Mi,*Mj, -33,33); KIHMD (window, *Mi,*Kj); if ( !KDVYES ("Подходящая маска?", ftbys) ) goto Lbeg; Для осуществления поэлементной коррекции используется не- небольшая подпрограмма KIMMIN, устроенная аналогично KDVINT. Для проверки перечисленных подпрограмм служит небольшая тестовая программа KIMMO, показанная на листинге 5.16. Листинг 5.14. Функция KIMMIN iinclude <stdio.h> •include "IHAGE.H" /¦ ФАЙЛ ~ KIKMII.C ¦/
196 Глава 5, Локальная фильтрация изображения void KIHNII (text, Hae,Ni,Ij, Iain,Ieax) char «text; /* Заголовок, до 40 символов */ char Has[]; /* Вводимый массив */ int Ii,Ij; /* Его размер: строк, столбцов */ int Xain,Xaax; /* Нижняя и верхняя границы */ /¦ ¦/ /* Диалоговая коррекция массива char */ /* Если Iain >¦ 1вах, контроль границ отюшчается */ /ф ¦/ { int i,j,k; int Iva; char digit[16]; printf (" Xs\n", text); for (k»0, i-1; i<«Ni; i*+, putsO"')) for (j«i; j<»Nj; j*+, «¦¦) Iva * Has[k]; loop { printf <•• H C5C3d,5C3d ] «Ш » ", i,j,Iva); gets (digit); if («digit) Iva * atoi (digit); if ( (Iain >« I»ax) II (Iva>«Iain kk IvaOIaax) ) break;; Iva « Has[k]; printf("%30s Допустимо от Xd до */,d \n", "", Iain^Iaax); } Has[k] « Iva; Листинг 5.15. Головной файл КШМ.Н /¦ ФАЙЛ — KIMH.H ¦/ int KIMHAT (int Hw, int *Mi, int *Hj); void KIMHD (char Mask[] , int Ni, int Nj); void KIHMDW (FILE *Fout, int left, char Hask[], int Ni, int Nj, int wl, int xOy); void KIHNFB (char Hask[], int Ni, int Nj); void KIMMFO (char Hves[9]); void KIMMFU (char Hask[], int Nv, int «Hi, int *Hj); void KIHHIN (char «text, char Hask[], int Hi, int Hj, int hmin, int haax);
5.7. Рекурсивные линейные фильтры 197 void KINHLI (char HaskD , int Mi, int Mj, int il, int jl, int i2, int j2, int L); void KIMHSH (char HaskD, int Mi, int Mj, int Hur); Листинг 5Л6. Тестовая программа KIMMO finclude <8tdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIHHO.C ¦/ /¦ Выбор маски */ void char Mas[50]; int Mi»3,Mj«3; loop { puts ("\n\n\tKIMM0 - Выбор маски (весовой функции)\n\n"); KINMFU (Has, 50, fcMi,fcMj); PAUSE. 5.7. Рекурсивные линейные фильтры Фильтр, в котором отклик определяется только через входные зна- значения, называется простым, или нерекурсивным. Именно такими были рассмотренные выше линейные фильтры. Рекурсивным называется фильтр, в котором отклик опреде- определяется не только через входные значения, но и через выходные [27, 33]. В рекурсивных фильтрах могут использоваться те лее весовые функции, что и в нерекурсивных. Дадим два определения. Рекурсивным фильтром 1-го рода на- называется такой фильтр, у которого отклик в каждом положении окна формируется так же, как у простого фильтра, но записы- записывается обратно во входной массив, который теперь одновременно играет роль выходного: мр мч «'. Л = Е ? F({ -P™+P,J-qm + q)H(p, q). E.8)
198 Глава 5. Локальная фильтрация изображения Формула аналогична E.1), поэтому сделанные замечания спра- справедливы и здесь. Рекурсивным фильтром 2-го рода называется комбинирован- комбинированный фильтр, у которого отклик формируется как взвешенная сум- сумма откликов нерекурсивного фильтра и рекурсивного фильтра 1-го рода: Д(», j) = kQ(i, j) + A - k)F'(i, j), E.9) где Q(iyj) определяемся из E.1), Ff(i,j) — из E.8), ib — коэффи- коэффициент, определяющий вклады каждого из двух упомянутых филь- фильтров. Варьируя величиной Jb от 0 до 1, можно плавно менять харак- характер фильтра; Jb = О соответствует простому фильтру, к = 1 — рекурсивному фильтру 1-го рода. Для рекурсивных фильтров 1-го рода нет необходимости пи- писать специальные программы — эти фильтры реализуются с помо- помощью уже рассмотренных нами программ простых, нерекурсивных линейных фильтров. Вы уже догадались, каким образом? Действительно, для это- этого достаточно при обращении к этим программам указать один и тот же массив как для входного изображения, так и для выход- выходного. Именно так сделано в тестовой программе, которая будет рассмотрена ниже. Подчеркнем (специально для программистов) — рекурсивный фильтр вовсе не предполагает рекурсивного вызова подпрограмм, т. е. понятие рекурсии имеет здесь несколько иной смысл, чем в программировании. На рис. 5.12 (сравните с рис. 5.7) показаны результаты приме- применения рекурсивного фильтра 1-рода к тестовому полю. На рис. 5.13 показан результат итеративного применения этого фильтра. После шести проходов изображение перестало изменять- изменяться, т. е. была достигнута стабильная точка фильтра. Добавим, что стабильные точки двумерных фильтров почти не исследованы. Рекурсивный линейный фильтр 2-го рода реализован подпро- подпрограммой KIFORS, показанной на листинге 5.17. Коэффициент к взят равным 1/2, что обеспечивает равные вклады простого и ре- рекурсивного фильтров. Подпрограмма имеет такой же набор фор- формальных параметров, как и KIFOLS. Массив Image служит как входным, так и выходным, что характерно для рекурсивных филь- фильтров, массив ImOut является вспомогательным. Он использует- используется для сохранения исходного поля при формировании в массиве Image промежуточного изображения рекурсивным фильтром 1-го рода.
5.7. Рекурсивные линейные фильтры 199 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 0 i i i i i i i i i i i i i i i . 1 . 2 . 2 . 2 . 2 . 2 . 2 . 2 . l . 1 2 л от ел ел i 5 5 5 4 1 2 5 6 6 7 7 7 7 4 1 5 * 2 5 6 7 7 7 7 7 5 2 2 5 6 7 7 7 7 7 5 2 2 5 6 7 7 7 7 7 5 2 2 5 6 7 7 7 7 7 5 2 2 5 6 7 7 7 7 7 5 2 10 2 4 4 5 5 5 5 5 3 1 1 1 2 . 2 . ? 2 ? 1 1 . 15 * . . 1 . . 1 . 1 2 1 ^ • X О . . 1 20 _________ i 11... 2 . . . . 2 2 2 1. о о о 1 о о « л • 111.. Рис. 5.12. Результаты рекурсивной фильтрации. Алгоритм KIFOLS, окно 3x3, маска вида E.3). С 1 1 2 ! 3 ' 4 ! 5 ! 6 ' 7 i 8 < 9 < 10 ! 11 12 ' 13 ' 14 ' 15 i 16 i ) __. i 1 i 1 ' 2 ! 2 ! 2 1 2 ! 2 1 2 1 2 ! 2 ! 1 > . 1 2 2 3 3 3 3 3 3 2 1 1 2 2 3 4 4 5 5 5 4 3 2 1 2 3 4 5 6 6 6 6 5 4 2 1 5 • 2 3 5 6 6 7 7 6 5 4 2 1 2 3 5 6 7 7 7 7 6 4 2 1 2 3 5 6 7 7 7 7 6 4 2 1 2 3 5 6 6 7 7 6 5 4 2 1 2 3 4 5 5 6 6 5 5 4 2 1 10 _ i. • 2 2 3 4 4 4 4 4 4 3 2 1 1 1 2 2 2 2 2 2 2 2 1 . 1 1 1 . 1 . 1 1 1 1 1 . . . 15 • . 1 . 1 . 1 1 1 1 1 1 1 1 1 2 2 1 • • 1 2 2 1 2С .____ i . . . . . . 1 . . 1 1 . 1 1 . 1 . . Рис. 5.13. Результаты итеративного применения рекурсивного фильтра. Стабильная точка достигнута после шести проходов. Ал- Алгоритм KIFOLS, окно 3x3, маска вида E.3).
200 Глава 5. Локальная фильтрация изображения О 5 10 15 20 1 2 3 4 5 6 7 8 Э 10 11 12 13 14 15 16 1122222211 1455555541 2566666642... 111.. 2567777752... 11... 2577777752 2577777752. 2577777752 2577777752 1445555531 1112222211.. 112211 123321 «.1111. Рис. 5.14. Результаты фильтрации. Рекурсивный фильтр 2-го рода KIFORS, окно 3x3, маска вида E.3). Подпрограмма KIFORS устроена следующим образом. Сначала в массив ImOut копируется исходное иоле из массива Image, после чего вызывается подпрограмма KIFOLS как рекурсивный фильтр. Теперь в массиве Image находится профильтрованное поле^ а в мас- массиве ImOut — остается исходное. Следующая группа операторов, организующая перемещение окна по изображению и обработку в окне, повторяет соответству- соответствующий участок подпрограммы KIFOLS. Эти операторы реализуют простор линейный фильтр. После того как отклик в некотором положении окна получен, он добавляется к значению соответству- соответствующего пикселя поля Image, Поле, полученное в результате фильтрации тестового изобра- изображения подпрограммой XIFORS, показано на рис. 5.14. Листинг 5.17. Функция KIFORS finelude <stdio.h> #include "IMAGE.H" /¦ ФАЙЛ — KIFORS.С */ void KIFORS (I»age,Hi,Nj, window,Hp,Nq, I«Out) SHOW lB&ge[], IaOut[]; char window [] ;
5.7. Г курсивные линейные фильтры 201 /* int Рекурсвший лввейвый фвл&тр 2-го рода. ¦/ Xaage Ii, lj vindov Ир, Hq XsOut /¦ /* /* /¦ /* /* —————«——»— •.«.— { int register p,q; int pl»p2, qi>q2: int zon; int г; int re: C0U1IT 1»; int io,jo; COUIT ко; СОШПГ к; int рв»(Мр+1)/2, int р!пввэв1п2.5*0; int шмш; неходкое» оно же выходное, взображевве размеры «сходного в врофкльтроваявего волей имзсовая фувкцвя окна р&змеры окна вспомогательный масевв для исходного поля /* /* /* /* /> ¦/ */ ¦/ */ */ явдексы окна. С строка, столбец) */ гравяцн язмевеяяя кядексов окна */ явдвкатор ввутреввей зовы поля */ текущвй ьявиент окна ¦/ вачало текущей строки окна */ то же, в коордвватах поля */ текущее положение центра оква */ текущее «оложежяе центра окна */ текумшй »лемев? поля ¦/ /¦ условяий явнтр оква ¦/ /¦ сумма элементов оква */ if ( Ы%%<в ж» I«Cut ) { puts<"KIFORS* некорректное кспользованже"); return; > /¦ Свачала - рекурсивный |шгьтр 1-го рода ¦/ for (k*Ii«Ij; k>0; } < k—; (liiage ,li5lj, ^indovfMprKq, Iaage); /* Теперь - простой |жяьтр ж суммяроаанве ¦/ г » Мр*1Ц; whale ( г > 0 ) vinzo ¦« eindow[~-r] ; if ( vinzo яяв 0 ) uinaio ¦ 1; for {ko»0,io*i; io<"Ii; pi * 1+pe-io; if ( pi < 1 ) p2 * Si+pe-io; if ( p2 >Mp ) zon * (pl*»l kk p2**Mp); pi » 1; p2 » Mp; for (jo*l; jo<* q2 , ko-н») if < ql < 1 ) if ( q2 >«q ) q2 1; Mq;
202 Глава 5. Локальная фильтрация изображения if (zon кк ql**l fcfc q2"-Mq) wine* * vinzo; { /¦ внешняя зона: п«р«вычнсля«м vines ¦/ wine* * 0; for (rs»(pl-l)«Mp*ql-l, p«pl; p<»p2; p*"*, rs+»Mq) for (r*re, q*ql; q<mq2; q++, vinsa +¦ window[r]; if ( vinsa ¦* 0 ) wine* * 1; sub * 0; ke ¦ MARK_(io-pa+pl,jo-qa+ql); for (rs»(pl-l)«Mp+ql-l, p*pl; p<»p2; p++, rs+»Mq, for (k*ke, rere, q»ql; q<*q2; q++, r++, k++) sub +* ( (int) IaOutCk] * (int) window[г] ); sub * (int)( (floa.t)suB / vinsa + 0.5 ); if ( sub < 0 ) sub * 0 ; lB&ge[ko] > ( I«Lge[ko] + sub ) / 2 ; Тестовая программа KIFO3F (листинг 5.18) предназначена для экспериментов по рекурсивной фильтрации изображений. Про- Программа по своим возможностям аналогична программе KIFO2F, описанной в разд. 5.5, но имеет существенные отличия по внутрен- внутренней структуре и по способу управления ходом обработки. Про- Программа KIFO3F, как и KIFO2F, позволяет циклически повторять опыты по фильтрации; каждый из опытов может включать фор- формирование исходного поля, выбор алгоритма фильтрации, созда- создание маски и обработку поля. В первом (после запуска програм- программы) цикле все перечисленные этапы выполняются последователь- последовательно. Впоследствии порядок обработки задается с помощью команд: I — выбрать поле; А — выбрать алгоритм; М — выбрать маску; G — фильтровать; W — выбрать маску и фильтровать; Е — завершить обработку. Для подачи команд используется функция KDVCHR (листинг 5.19), осуществляющая ввод символа.
5.7. ^курсивные линейные фильтры 203 Выполнение команд реализовано с помощью переключателя (оператор switch). Обратите внимание — перед выходом из некото- некоторых ветвей переключателя мы изменяем переменную переключа- переключателя, меняя тем самым пункт меню, который будет предложен по умолчанию в следующем цикле. Так, после того как была подана и выполнена команда I — выбрать поле, будет предложена коман- команда А — выбрать алгоритм. После выполения этой команды будет предложена команда G — фильтровать. Такая «интеллектуализа- «интеллектуализация» программы позволяет сократить число нажатий на клавиши в ряде стандартных ситуаций — ведь для того чтобы воспользо- воспользоваться предлагаемой по умолчанию командой, достаточно нажать клавишу <enter>. Возможны комбинированные команды, заменяющие две или несколько простых. Так, в нашей программе команда W объеди- объединяет команды А и G, выполняемые последовательно. При работе с программой KIFO3F надо помнить, что исходное изображение не сохраняется, так как рекурсивные алгоритмы в нашей реализации записывают результат фильтрации обратно в исходное поле, изменяя его. Поэтому каждый опыт следует начи- начинать созданием нового поля по команде I. Из сказанного также следует, что данная программа может по- послужить (хотя это и не было специально запланировано) удобным инструментом для изучения итеративного применения рекурсив- рекурсивной фильтрации; достаточно лишь НЕ подавать команду I. Один из полученных результатов показан на рис. 5.13. Листинг 5.18. Тестовая программа KIFO3F finelude <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIF03F.C ¦/ •include "RDV.H" /* Фильтрация изображений */ /* скользящим окном */ •define ALGMAX 7 /¦ Количество алгоритмов в меню */ •define LEWWII 64 /* Накс. число элементов окна */ char Mask С LEVWIV 3; char repeat ¦ 0, cfi ¦ »I\ ош » '¦»; char «fit * "KIF03F.DAT"; /* Имя файла вывода */ FILE *Fout, *fopen(); static char «algorithm [ ALGHAX+1 ] ¦ { "", "KIFILP", "KIFILS", "KIFILT", "KIFOLP", "KIFOLS", "KIFOLT", "KIFORS" }; void sainO { SHOV *Iaage, *Ia0ut;
204 Главе 5. Локальная фильтрация изображения int Ii-16, Mj»20, Mi»3, Mj-3, lv-7; int numal*l> Mu*Exp»l; COUIT need; char go»'i'; put* ("\n\n\tKIF03F - Фильтрация изображений скользящжн окном."); puts ("\t\t Рекурсивные фильтры"); if ( RDVYESC'CoxpaHiiTb лв результаты в файле?11, kcfi) ) { if ( cfi — 'у' ) cfi - »Y»; Fout * fopen (fit,"*"); if ( Fout ) printf ("XtBuBOfl результатов в файл Xs\n", fit); else { printf ("Файл %s не открывается!", fit); STOP. > KDVIIT ("Начать нумерации протоколов c"t fcMumExp, 0,9999); BEGII: if ( repeat ) { puts ("\n\t\t\tСпектр возможностей:"); puts ("\t I - выбрать поле"); puts ("\t A - выбрать алгоритм"); puts ("\t И - выбрать маску"); puts ("\t Q - фильтровать"); puts ("\t W - выбрать алгоритм И фильтровать"); puts ("\t E - завершить"); KDVCHR ("Что делать? <I/A/M/G/W/E>", *go); } switch ( go ) { case »I»: case >i': /¦ ¦/ /¦ Распределение памяти и создание поля */ if ( repeat ) { puts(cBo6oflHM память"); free (Image); } KDVIIT ("Высота поля, строк Mi", fcli, 0,512); KDVIIT ("Ширина поля, столбцов Mj", 41j, 0,512); need "Mi * Mj; puts ("Выделим память"); if (!(Image»malloc(need))) { puts("Нет места для Image!"); STOP. > if ( RIHGAU (Image, Mi,Mj, ftlv) -- 3) { if (Mi>8 fcfc Mj>10) { Image[5«Mj-5] » lv; Image[(Mi-4)«Mj-3] » lv; Image[(Mi-4)«Mj-4] » lv; Image[(Mi-4)«Mj-5] - lv; Image[(Mi-4)«Mj-e] » lv; > > ¦ puts(" Исходное изображение"); KIOTC(Image,Mi,Mj); PAUSE, if (cfi»»>Y> fcfc KDVYES("Записать это поле в файл?", fccmm) ) KIOFPG (Fout,0,Image, Mi,Mj, l,Mi, l,Mj, 2,79,60);
5.7. Рекурсивные линейные фильтры 205 go«'A'; if (repeat) break; case 'A': case 'a': /¦ ~ ¦/ case *Ы*: case >ч>: /¦ Выбор алгоритма фяльтации */ puts ("\n\t Выберите алгоритм фильтрации"); puts <"\t РЕКУРСИВНЫЕ АЛГОРИТМЫ 1-го РОДА:"); puts <"\t 1. KIFILP - НЧ-фильтр"); puts ("\t 2. KIFILS - НЧ-фильтр"); puts ("\t 3. KIFILT - НЧ-фильтр"); puts ("\t 4. KIFOLP - линейный фильтр с произвольной маской"); puts ("\t S. KIFOLS - линейный фильтр с произвольной маской"); puts ("\t 6. KIFOLT - линейный фхльтр с произвольной маской"); puts ("\t РЕКУРСИВНЫЙ АЛГОРИТМ 2-го РОДА:"); puts ("\t 7. KIFORS - линейный фильтр с произвольной маской"); KDVIIT ("Ваш выбор?", fcmmal, 1,ALGMAX); if ( go»»'W II go»»'*' ) goto LgG; goM)G>; if (repeat) break; case *M': case '¦': /¦ ¦/ /¦ Выбор маски */ if ( mmal<»3 ) KIMMAT (LEHWII, *Mi,*Mj); else KIMMFU (Mask, LEIWII, fcMi,Wtj); go«'G'; if (repeat) break; case 'Q': case 'g': /¦ — ¦/ LgG: /* Фильтрация исходного поля */ switch (nuaal) { case 1: KIFILP (Iaage, Ii,Nj, Mi,Mj, Isage); break; case 2: KIFILS (Iaage, Ii,Ij, Mi,Mj, Iaage); break; case 3: KIFILT (Image, Mi,Mj, Mi,Mj, Iaage); break; case 4: KIFOLP (Ieage,Ki,Mj ,Mask,Mi,Mileage); break; case 5: KIFOLS (lBage,Ni,Mj,Mask,Mi,Mj,lBage); break; case 6: KIFOLT (lBage,Mi,Vj,Mask,Mi,Mj,lBage); break; case 7: if (!(lBOut»Balloc(need))) { puts("Нет места для ImOut!"); STOP. } KIFORS (lKage,*i,Mj,Mask,Mi,Mj,lBQut); free (IaOut); break; default: puts ("Где-то вы напортачили ..."); break; } puts("Профильтрованное поле"); KIOTC (lBage,Ni,Ij); PAUSE, if (cfi**'Y> kb KDVYES(anHcaTb результаты в файл?", кспт) ) { if ( nuaal >> 4 ) KIMMDU (Fout,8,Nask,Mi,Mj, 3, 1); KIOFPG (Fout,0,lBage, Ii,Ij, l,Ii, l,lj, 2,79,60); fprintf(Fout,"\nPHC. S.Xd. %в\пАлгоритм %s, окно XdxXdAn", NueExp++,"Результаты фильтрации.", algorithe[nuaal],Mi,Mj); } repeat-*-*-; bre ak;
206 Глава 5. Локальная фильтрация изображения са»е *Е*г case 'в': /¦ * ¦/ goto EID; default: puts ("Такой альтернативы нет"); } goto BEQIH; EID: fr«« (Inage); if < cfi ¦¦ >Y> ) < fprintf(Fout,"\n*\n"); fcloee(Fout); } EID, Листинг 5.19. Функция KDVCHR •include <string.b> finclude <*tdio.h> /¦ ФАЙЛ — KDVCHR.С */ •include "KDV.H" iat KDVCHR (text,Cvar) char «text; /* Сообщение, до 40 символов */ char *Cvar; /¦ Введенный символ */ /* Диалоговый ввод символа */ /¦ Возвращает: 0 - если сохранен предложенный символ */ /е 1 - если введен другой символ */ /* .. -- : ^ ф/ < char dig[4]; printf <"X46.4Sa %c » ".text^Cvar); gets (dig); if ( strleiKdig) ) { *Cvar«digC03; return 1; } else return 0; 5.8. Нелинейные фильтры Основные понятия теории локальной фильтрации, изложенные в начале главы, справедливы и для локальной нелинейной фильтра- фильтрации. Имеются в виду понятия процесса фильтрации, апертуры (двумерной и «линейной»), способы и алгоритмы перемещения апертуры по изображению; весовая функция применяется не все- всегда. Главное отличие состоит в том, что выход нелинейного филь- фильтра формируется нелинейным образом от данных исходного изо- изображения.
5.8. Hei» шейные фильтры 207 Мы рассмотрим два класса нелинейных фильтров, используе- используемых для достижения в некотором смысле противоположных це- целей. Это медианные фильтры, применяемые для сглаживания изображений, и фильтры, подчеркивающие перепады яркости. К последним относятся фильтры Робертса и Собела, родственные в идейном плане. Помимо этого, описан фильтр, выявляющий ло- локальные максимумы изображения. Начнем изложение с медианного фильтра, отклик которого ра- равен медиане данных, находящихся в апертуре. Медиана предста- представляет собой центральный элемент в вариационном ряду, получен- полученном из данных, находящихся в пределах апертуры. В силу того что для операции нахождения медианы не выполняется одна из аксиом линейности, медианный фильтр является нелинейным [5]. Медианные фильтры применяются для сглаживания изобра- изображений и для подавления шума. Ранее мы рассмотрели линейные низкочастотные фильтры, применяемые для тех же целей. Меди- Медианные фильтры по своим свойствам отличаются от них [5, стр. 342; 34, стр. 156]. Во-первых, медианные фильтры сохраняют резкие перепады, тогда как линейные низкочастотные фильтры их сма- смазывают. Во-вторых, медианные фильтры очень эффективны при сглаживании импульсного шума, но (и это обратная сторона дан- данной медали) могут приводить к полному исчезновению мелких де- деталей изображения при неадекватном выборе параметров филь- фильтра. Медианные фильтры используются также для обнаружения границ и выделения объектов [34, стр. 186]. Для формирования апертур произвольной формы (крестооб- (крестообразных, кольцевых и т. д.) используется бинарная весовая функ- функция, принимающая значения 0 или 1. Медианные фильтры нередко применяются итеративно, при- причем фильтрация повторяется до тех пор, пока на профильтрован- профильтрованном изображении не прекратятся изменения. В другом вариан- варианте итеративного применения от шага к шагу итерации меняет- меняется апертура фильтра. В так называемом разделимом медианном фильтре одномерный медианный фильтр применяется сначала к каждой строке, а затем — к каждому столбцу изображения [34, стр. 183]. Разновидностью медианного фильтра является взвешенно-ме- взвешенно-медианный фильтр [34, стр. 182]. В таком фильтре используется весовая функция, но интерпретируется она иначе, чем в линейных фильтрах. Здесь весовые коэффициенты показывают, сколько раз следует учитывать пиксели изображения, попавшие в апертуру. Если выходу фильтра присваивать не значение медианы дан-
208 Глава 5. Локальная фильтрация изображения О 5 10 15 20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .777777. 77777777 77777777 77777777 77777777 77777777 77777777 .777777. Рис. 5.15. Результаты фильтрации. Медианный фильтр KIFMES, окно 3x3, маска вида E.2). ных, находящихся в апертуре, а значение любой г-& (г = 1,2,... п, где п — общее число элементов апертуры) порядковой статистики, то можно получить п фильтров, названных процентильними [34, стр. 190]. Конечно, они, как обобщение медианного фильтра, так- также являются нелинейными. Пользуясь случаем, напомним, что г-я порядковая статистика определяется как r-е по значению чи- число из данных п чисел. На рис. 5.15 показаны результаты медианной фильтрации те- тестового поля. Эти результаты получены с помощью подпрограммы KIFMES, описанной в следующем пункте вместе с другими про- программами нелинейных фильтров. Вкратце остановимся на медианных фильтрах для бгнарных изображений. Фильтрацию бинарных изображений обычно отна сят к нелинейным операциям [25]* Рассмотренный выше общий метод пригоден и для бинарных изображений, однако для них целесообразно использовать другое, упрощенное правило формирования отклика, не требующее по- построения вариационного ряда. Это правило состоит в следующем. Бели на участке поля, попавшем в апертуру, количество единиц превышает количество нулей, то отклик фильтра полагается рав- равным единице, в противном случае — нулю.
S.8. Нелинейные фильтры 209 О 5 10 15 20 1 ! 2 ! 3!... 111111 4!. .11111111 5!.. 11111111 6!. .11111111 7?. .11111111 8!. .11111111 9!. .11111111 10 ! ... 1 1 1 1 1 1 11 ! . . . 12 !..... 13 ! 14 ? 15 ! 16 ! Рис. 5.16. Результаты фильтрации. Бинарный медианный фильтр KIFMBS, окно 3x3, маска вида E.2). Данное правило допускает обобщение на случай бинарного про- центильного фильтра. В таком фильтре отклик полагается рав- равным единице, если в пределах апертуры находится по крайней мере г единиц (г = 1,2,... п, где п — общее число элементов апер- апертуры). На рис. 5.16 показаны результаты бинарной медианной филь- фильтрации тестового поля. В разд. 5.4 рассматривались линейные методы (и соответствен- соответственно линейные фильтры) подчеркивания перепадов яркости и выде- выделения контуров. Известны и нелинейные методы контрастирова- контрастирования, некоторые из которых мы рассмотрим. Один из таких методов предложен Робертсом [5, стр. 508] и состоит в использовании операции двумерного дискретного диф- дифференцирования GR(i,j) = y/U* + V*, E.10) где Сходные результаты дает другой аналогичный оператор, тре-
210 Глава 5. Локальная фильтрация изображения бующий меньшего объема вычислений: E.11) Из формул E.10) и E.11) видно, что Роберте применяет кваг дратную апертуру размером 2x2. Весовая функция не задается (подразумевается, что она тождественно равна единице). Результаты фильтрации по Робертсу показаны на рис. 5.17а (используется метрика вида E.10)) и 5.176 (метрика E.11)). Другой нелинейный оператор контрастирования, использую- использующий апертуру размером 3x3, предложен Собелом [5, стр. 509]: E.12) где X = Y = [F(i - 1, з - 1) + 2F(» - 1, j) + F(i -1J + 1)] - По аналогии с E.11) напрашивается упрощенный вариант, не тре- требующий операций возведения в квадрат и извлечения корня: GT(i,j) = \X\ + \Y\. E.13) Результаты фильтрации по Робертсу показаны на рис. 5.18а (используется метрика E.12)) и 5.186 (метрика E.13)). Методы нелинейной обработки чрезвычайно разнообразны. В качестве еще одного примера приведем алгоритм, который выявля- выявляет имеющиеся на изображении локальные максимумы (вершины). Алгоритм несложен. По изображению перемещается окно за- заданного размера. Пиксели, попавшие в окно, сравниваются по величине с пикселем, соответствующим центру окна. Если цен- центральный пиксель больше или равен своему окружению, то он без изменения переносится в выходное изображение. Бели хотя бы один пиксель в окне превышает центральный, в выходное поле за- записывается нуль. Результаты применения такого алгоритма к псевдослучайному тестовому изображению (рис. 1.6) показаны на рис. 5.19 для двух различных размеров окна.
5.8. Нелинейные фильтры 211 О 5 10 15 20 I. —— — -.1. — — 1 • * т 9 • 1 ! 21.799999997 3 • . 9 9 5 1 о о 7 7 • a^asaaaaa^aaaatfaaaa 6 ! . 9 9 7 ! . 9 9 8 ! . 9 9 9 ! . 9 9 10!.799999997 11! 79997.. 12! 79997.. 13 ! 14 ! 15 ! 16 ! Рис. б.17а. Результаты фильтрации по Робертсу. Алгоритм KIFGRP, окно 2x2. 0 5 10 15 20 • • • • • 1 ! 2! . 7еееееее7 3!.е е 4 1 е в 7 7 О..ваа*а«аав.аааГГааа« 6!.е е 7!. в е 8!. в е 9!. в е 10! . 7ввеевев7 11! 7вее7.. 12! 7ввв7.. 13 ! 14 ! 15 ! 16 ! Рис. 5.176. Результаты фильтрации по Робертсу. Ускоренный ал- алгоритм KIFGAP, окно 2x2.
212 Глава 5. Локальная фильтрация изображения 10 IS 20 1 ! 2 ! . 9i8ii8Sii9 3! .mtftftftssatm 4!. ее ss...9e9.. о «.вв. .....в в • . «v.v. . 6 1 __ — — О _Ь О • .Во. . . . . •—>*}• . . 9 V 7 . . г.. ВВ.. ....ВВ........ 8 ! . 8 8 88 9 ! . 8 8 88 10! .Mt8S8888tM 11! . 9Ш888888Ш9. . 9 Ш 8 8 Ш 9 12 ! в в . . е в 13! 9в88в9 14 ! 15 ! 16 ! Рис* 5.18а. Результаты фильтрации по Собеду. Алгоритм KIFGSP, окно 3x3. 10 15 20 1 ! 2! .6888888886 3! .sGssssssGs 4!. 88 88...еее.. 5!. 88 8 8 . . , 6 . 6 . . €! .88 88. . .еее. . 7!. 88 88 8 ! . 8 8 88 9!. 88 88 10! .sGssssssGs 11! .6888888886. .688886 12 ! 6 6 . . 6 6 13! 688886 14 ! 15 ! 16 ! Рис. 5.186. Результаты фильтрации по Собелу. Ускоренный алго- алгоритм KIFGTP, окно 3x3.
5.8. Нелинейные фильтры 213 О 5 10 15 20 1!7.а 9.аа.. 2! а. а а... 3 ! .... 8 4!. .а. ..а 9 5! 9. .99 8 6! 9. ..8 7! 9999.6 8!.. ..а 6.7.. 9! а. ...8 8 IV ... а . в ....о.... в . . . . • XX. а..... а.... о*. ..«?••• 12 ! . . . . а . . а 9 13 ! а . 14 ! • • а а.8..а..а«. 1С | Q Q а я Q Aw . v ...... & . С& . . » . С& .... v 16 ! ... 8 • а а.... Рис. 5.19а. Результаты выявления вершин на поле с рис. 1.6. Ал- Алгоритм KIFHYS, окно 3x3. t 2 3 5 6 7 8 9 10 11 12 13 14 15 16 0 ; i i ! . . i i i i i ! . . ! а . i ! . . i i а • а . а • 5 • а а а 10 mm шмш. | ттттш * • а • а • • .... 9 . .... 9 9 • а • ... • а • ... • . а ... • • • • а • . • • • а . а 15 20 ....... .........i аа.. . . 9 .99 8 ... а а. . . . . а . . . . Рис. 5.196. То же, для окна 5x5.
214 Глава 5. Локальная фильтрация изображения 5.9. Программы нелинейных фильтров Подпрограмма KIFMES (листинг 5.20) реализует взвешенно-меди- взвешенно-медианный фильтр. Бе параметрами являются: исходное поле, его раз- размеры, весовая функция (маска), ее размеры, выходное поле; всего 7 параметров. Выходное поле имеет такие же размеры, как вход- входное. Задавая различные весовые функции, можно управлять фор- формой апертуры, а также получать как обычный медианный фильтр, так и взвешенно-медианный. Для того чтобы получить обычный (т. е. не взвешенный) медианный фильтр, надо задать маску, со- состоящую либо только из единиц (для получения прямоугольного окна), либо из единиц и нулей (для формирования апертуры про- произвольной формы). При обращении к подпрограмме не допускается использовать маски, содержащие отрицательные элементы, и маски, состоящие из одних нулей; если задана недопустимая маска, происходит вы- выход из подпрограммы без обработки, но с предупреждающим со- сообщением на экране. Подпрограмма KIFMES использует алгоритм, непосредственно основанный на приведенном выше определении медианного филь- фильтра. Отклик вычисляется для каждого положения окна следую- следующим образом. Значения (яркости) пикселей, попавших в окно, переписываются во вспомогательный массив varytab, причем за- записывается столько копий каждого пикселя, чему равно значение соответствующего элемента маски. При нулевом значении маски соответствующий пиксель не копируется, при единичном — копи- копируется один раз и т. д. В ходе копирования подсчитывается коли- количество заполненных ячеек массива, дающее длину вариационного ряда. Поскольку используется 5-схема движения окна, это коли- количество сохраняется постоянным только в центральной зоне, а вне ее может изменяться. Затем полученный таким образом массив упорядочивается с помощью функции быстрой сортировки qsort, определяемой стан- стандартом языка Си. После сортировки массив varytab содержит ва- вариационный ряд. Остается взять центральный элемент этого ря- ряда, и медиана определена. При четном числе членов на роль ме- медианы претендуют сразу два центральных члена. В литературе существуют различные определения для этого случая. Мы берем левый из двух центральных членов вариационного ряда. При выбранном нами определении медианы ее нахождение сво- сводится к целочисленному делению на 2 числа членов wa вариаци- вариационного ряда. С учетом того что нумерация элементов массивов
5.9. Программы нелинейных фильтров 215 в Си начинается от нуля, медиана содержится в ячейке (wa-l)/2 массива vary tab. В случае если вариационный ряд пуст (что может произойти в отдельных точках при какой-нибудь экзотической маске), отклик фильтра полагается равным нулю. Для сравнения двух членов вариационного ряда подпрограмма использует функцию compare, находящуюся в том же файле. Эта функция является параметром функции быстрой сортировки. Результат фильтрации тестового поля подпрограммой KIFMES показан на рис. 5.15. Листинг 5.20. Функция KIFMES finclude <stdio.h> finclude "IMAGE.H" /* ФАЙЛ — KIFMES.С ¦/ static int сошралге (x,y) /* Функция сравнения для qsort */ SHOW *x,*y; { return (int) ( *x - *y ); void KIFMES (Image,Ni,Nj, window,Mp,Mq, ImOut) SHOW Imaged, ImOut[]; char window [] ; int Ni,Mj,Mp,Mq; /«, „ «,/ /* Фильтрация скользящим окном - на плоскости */ /* ВЗВЕШЕННЫЙ МЕДИАННЫЙ ФИЛЬТР */ /* Центр окна пробегает все поле, перемещаясь построчно. */ /* Окно МОЖЕТ выходить за пределы поля, */ /* но обрабатывается только часть окна, лежащая на поле. */ /* Image - исходное изображение */ /* Mi, Nj - размеры исходного и профильтрованного полей */ /* window - весовая функция окна */ /* Мр, Mq - размеры окна */ /* ImOut - профильтрованное поле */ /«, ф/ { int register p,q; /* индексы окна (строка, столбец) */ int pl,p2, ql,q2; /* границы изменения индексов окна */ int zon; /* индикатор внутренней зоны поля */ int r; /¦ текущий элемент окна */ int rs; /* начало текущей строки окна */
COUIT ke; int io.joj COUIT ко; COUIT k; /¦ /¦ /¦ /¦ 216 Глава 5. Локальная фильтрация изображения то n i координатах поля ¦/ текущее положение центра окна */ текущее положение центра окна */ текущий элеиент поля */ int ря*(Мр+1)/2, qa*(Mq+l)/2; /* условный центр окна */ int vinzo; /* сумма элементов окна ¦/ int va; /¦ текущий член вариационного ряда */ int h; SHOW «тагуtab; extern int соираге О; pute("\nKIFMES: взвешенный медианный фильтр"); if ( I«age »~ IaOut ) puts ("^Рекурсивный фильтр"); for ( r*Mp*Mq-l; r>«0; r— ) { p»window[r]; vinzo +* p; if ( p<0 ) { put»("Отрицательный весовой множитель недопустим"); return; } } if < winzo<»Q ) { puts("Пустая маска не допускается"); return; } if ( I ( varytab«Balloc<eizeof(SHOW) ¦ (winzo*!)) ) ) { puts("Нехватка памяти для внутренней таблицы"); STOP. > for (ko*0tio*l; io<«Ii; pi ¦ И-рв-io; if ( pi < 1 ) pi « 1; p2 * Ii*pB-io; if ( p2 >Hp ) p2 » Mp; for (jo«l; jo<*Ij; jo*+t ql * l*q*-jo; if ( ql < 1 ) ql « 1; q2 « Ij+q«-jo; if ( q2 >Mq ) q2 * Hq; ?a * 0; k8 * for (re»(pl-l)*Mp+qi-l, p*pl; p<»p2; for (k"X8, r*re, q*ql; q<»q2; for (h»eindow [r] ; h>0; h—) varytabCwa**] * I»age[kj;
5.9. Программы нелинейных фильтров 217 if (»а> { qeort (varytab, vt, sizeof(SHOW), compare); leOut [ ко 3 « Tarytab С (va-l)/2 ]; } •lee IiiOut [ ко 2 * 0; free (тагуtab); Подпрограмма KIFMHS (листинг 5.21) также реализует ьзве- шенно-медианный фильтр, но использует метод, основанный на получении текущей гистограммы. Подпрограмма имеет такой лее список параметров, как и KIFMES. Результаты фильтрации также совпадают. Сравните, какая из двух подпрограмм быстрее выполняется на вашем компьютере. Зависит ли преимущество от размеров окна? В какой из подпрограмм имеются значительные резервы повышения быстродействия? В чем они состоят? Листинг 5.21. Функция KIFMHS tinclude <8tdio.h> «include "IMAGE.H" /¦ ФАЙЛ — KIFMHS.С ¦/ void KIFHHS (Ia&g«,Hi,Ij, шпион fHptHqt IftOut) SHOH ХмдоП. X«0utO; char vindov 0; int Ii,Ij,Kp,Kq; /* Фильтрация скользящим окном * на плоскости ¦/ /* ВЗВЕ1ЕЕНУЙ ШШАНКЫЙ ФШЬТР - через гистограмму */ /* Центр окла звроб^гает see поле* перемещаясь построчно. ¦/ /* Окно МОЖЕТ выходить за пределу поля, ¦/ /* но обраб&тмяается только часть о^.ва., лежащая на поле, */ /¦ Iaage - исходное изображение ¦/ /* !i, Ij - размера исходного и профильтрованного полей ¦/ /* urindov - шфссшшм функция окна */ /* Нр» Mq - раэм^рк окна ¦/ /* XttOut - профильтроаатгое поле */ < int register p,qr /* индекса окна (строка, столбец) ¦/
218 Глава 5. Локальная фильтрация изображения границы изменения индексов окна */ индикатор внутренней зоны поля */ текущий элемент окна */ начало текущей строки окна */ то же, в координатах поля */ текущее положение центра окна */ текущее положение центра окна */ текущий элемент поля */ int р«ж(Нр+1)/2, q«»(Hq+l)/2; /* условный центр окна */ int vinzo'O; /* сумма элементов окна */ int wa; /* текущий член вариационного ряда */ int h; int Hist [ МШШХ+1 ] ; puts("\nKIFHHS: взвешенный медианный фильтр"); if ( inage жж laOut ) puts("\tРекурсивный фильтр"); int pl,p2, ql,q2; int zon; int r; int rs; COUIT ks; int io,jo; COUIT ko; COUIT k; /¦ /¦ /¦ /¦ /¦ /¦ /¦ /¦ for ( r«Mp*Hq-l; г>»0; г— ) { p»window[r]; vinzo ¦¦ p; if ( p<0 ) { puts("Отрицательный весовой множитель недопустим"); return; } } if ( winzo<«0 ) { puts ("Пустая маска не допускается11); return; } for (ko»0,io«l; io<«Ii; io-м-) { pi ¦ 1+pa-io; if ( pi < 1 ) pi ¦ 1; p2 » li+pe-io; if ( p2 >Mp ) p2 » Hp; for (jo«l; jo<«Ij; jo-м-, ko-м-) { ql » H-qa-jo; if ( ql < 1 ) ql * 1; q2 » Ij+q»-jo; if ( q2 >Hq ) q2 * Mq; wa-0; for (h»MURMAX; h>»0; h—) HistCh] » 0; ks ¦ MARK_(io-pB*pl,jo-qB*ql); for (rs«(pl-l)«Mp*ql-l, p«pl; р<жр2; for (k*ks, r»rs, q*ql; q<*q2; q*+, { Hist [ Iaage[k] ] ¦¦ window[r]; wa ¦¦ vindov[r]; } if (wa) { for (h»0, wa*(wa-H)/2; wa>0; h-м-) wa -» Hist[h]; I»0ut [ ko ] » h-l; } else IaOut [ ko ] » 0;
5.9. Программы нелинейных фильтров 219 Подпрограммы KIFMES и KIFMHS пригодны, конечно же, и для бинарных изображений, однако в этом случае лучше приме- применять подпрограмму KIFMBS (листинг 5.22). Она реализует упро- упрощенное правило обработки, описанное в предыдущем пункте. От- Отметим, что это правило напоминает построение гистограммы из двух столбцов. Для подпрограммы KIFMBS любое поле — бинар- бинарное, так как она различает лишь два уровня: нуль — не-нуль. Вы- Выходное поле состоит из нулей и единиц. Результат фильтрации тестового поля подпрограммой KIFMBS показан на рис. 5.16. Листинг 5.22. Функция KIFMBS finclude <etdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIFMBS.С ¦/ void KIFMBS (I»age,li,lfj, window,Hp,Mq, ImOut) SHOW Iiage[], InOut[]; char vindovO ; int Ii,Vj,Mp,Mq; /¦ Фильтрация скользящим окном - на плоскости */ /* ВЗВЕШЕННЫЙ БИНАРНЫЙ МЕДИАННЫЙ ФИЛЬТР */ /* Центр окна пробегает все поле, перемещаясь построчно. */ /* Окно MOIET выходить за пределы поля, */ /* но обрабатывается только часть окна., лежащая на поле. */ /* In&ge - исходное изображение */ /* Hi, Ij - размеры исходного и профильтрованного полей ¦/ /* window - весовая функция окна */ /* Ир, Mq - размеры окна */ /* IeOut - профильтрованное поле */ int register p,q; /* индексы окна (строка, столбец) */ int pl,p2, ql,q2; /* границы изменения индексов окна. */ int zon; /* индикатор внутренней зоны поля */ int г; /* текущий элемент окна */ int re; /* начало текущей строки окна ¦/ СОШГТ ks; /* то же, в координатах поля */ int io,jo; /* текущее положение центра окна */ COUIT ко; /* текущее положение центра окна */ COUWT к; /* текущий элемент поля */ int ра»(Мр*1)/2, q*«(Mq"»-l}/2; /* условный центр окна. */ int winzo»0; /* сумма элементов окна */ int Hist[2];
220 Глава 5. Локальная фильтрация изображения put#("\nKIFMBS: взведенный бин&ряый медианный фильтр"); if ( Ieage ¦» IftOut ) put*("\tP«KypcBBHuft фильтр'1); for ( ar»Mp*Mq-l; r>«0; r— ) { p*window[r]; vinzo ¦« p; if ( p<0 ) < puts("Отрицательный весовой множитель недопустим"); return; } if ( trinzo<«0 ) { put»("Пустая маска не допускается"); return; } for (ko«0,io«l; io<«Ii; io-м-) { pi * l+pe-io; if < pi < i ) pi « 1; p2 « Ii+pi-io; if ( p2 >Hp ) p2 « Mp; for (jo*l; jo<»Ij; jo-n-, ko++) { ql « l*q«-jo; if ( ql < 1 ) ql « 1; q2 « Ij+qa-jo; if ( q2 >Mq ) q2 « Kq; Hist[0] * Hiet[l] * 0; ke ¦ HARK.(io-p«*pl,jo-q»4ql); for (re«(pl-l)«Mp*ql-l, p«pl; p<«p2; for (k«ks, r»re, q*ql; q<»q2; q++, Hiet [ (Image[k]) ? 1 : 0 3 ¦« window[r]; IaOutCko] * ( Hiet[l] > HietCO] ); Рассмотренные подпрограммы легко могут быть обобщены на случай процентильного фильтра. При таком обобщении следует учесть, что в S-схеме перемещения окна длина вариационного ря- ряда не является постоянной. Задавать номер порядковой статисти- статистики при переменном числе членов вариационного ряда — не лучшее решение. Удобнее задать долю процентильности, т. е. относитель- относительный уровень, определенный в процентах от числа членов вариа- вариационного ряда. Доля процентильности, равная 50, соответствует медианному фильтру. Описанные функции вызываются в тестовой программ^ KIFM4F (листинг 5.23), которая позволяет проконтролировать их
5.9. Программы нелинейных фильтров 221 работу и получить на экране картинки, приведенные выше. Про- Программа аналогична по своему устройству программе KIFO3F, опи- описанной в разд. 5.7. Программа KIFM4F позволяет также наблю- наблюдать работу подпрограмм KIFMES и KIFMBS, вызванных как ре- рекурсивные фильтры 1-го рода. Листинг 5.23. Тестовая программа KIFM4F finclude <stdio.h> «include "IMAGE.H" linclude "KDV.H" /¦ ФАЙЛ — KIFH4F.C */ /* Фильтрация изображений */ /¦ скользящий окном ¦/ «define ALGMAX 5 «define LEIWII 64 /¦ Количество алгоритмов в меня */ /¦ Макс, число »лементов окна ¦/ char Mask [ LEIVII ]; char repeat « 0, cfi * 'I', cbb « 'I'; char «fit - "KIFM4F.DAT"; /* 1мя файла вывода */ FILE *Fout, *fopen(); char «algorithn [ ALGMAX+1 ] * { ini, "KIFMES", "KIFMHS", "KIFMBS", "KIFMES", "KIFMBS" }; void HainO { SHOV *I»age, *I»0ut; int Ii«16, Ij«20, Mi«3, Mj«3, int nuaal'l, Iu«Exp»l; COUIT need; char go«'i'; lv-7; puts ("\n\n\tKIFM4F - Фильтрация изображений скользящий окном"); puts ("\t\t Простые и рекурсивные медианные фильтры"); if ( KPVYESC"Сохранять лн результаты в файле?", fccfi) ) { if ( cfi « 'у' у' ) cfi « 'Y'; Fout ¦ fopen (fit/V); if ( Fout ) printf ("XtBbiBOA результатов в файл %s\n", fit); else { printf ("Файл %в не открывается!", fit); STOP. } KDVIHT ("Начать нумерации протоколов с", fclunExp, 0,9999);
222 Глава 5. Локальная фильтрация изображения BEQII: if С repeat ) { puts ("\n\t\t\tCnexTp возможностей:"); put» <"\t I - выбрать поле"); puts ("Vt A - выбрать алгоритм"); puts ("\t И - выбрать маску"); puts ("\t Q - фильтровать"); puts ("\t W - выбрать алгоритм И фильтровать"); putt <"\t Б - завершить"); «XDVCHE ("Что делать? <1/А/М/О/Ы/Е>", ego); switch С go ) { case »P: case >i;: /¦ ¦/ /* Распределение памяти и создание поля */ if ( repeat ) { puts("Освободим память"); free (Image); free (IbOuO; } KDVIIT ("Высота поля, строк li", *Mi, 0,512); KDVIMT ('Лирика поля, столбцов Hj", *»j, 0,512); need * Hi * Hj; puts ("Выделим память"); if (!(XBage*»all©c(iseed))) { put»("Нет места для Iaage!"); STOP. } if (!(lBOut»Balloc(need))) { put<*("HeT места для iBOut!11); STOP. > if ( KIMOAU (Ieage, li vj, ftlv) »« 3) { if (Mi>8 kk »j>10) . lBage[5«Ij-5] * It; lBage[(»i-4)*»j-3] * Iv; lBage[(Ii-4)*Ij-43 ¦ It; lBage[(Ii-4)«Ij-53 - lv; lBage[(Ii-4)*»j-e] « piste(H Sсходное изображение11); KIOTC(lBage,Mi,Ij); PAUSE, if (cfi*""Y' kk RDVYES("Записать это поле в файл?1', *свв) ) KIOFPQ (Fout,QjHage, Ii,llj, l,Mi, i,Mj, 2,79,60); go»'A'; if (reoeat) break; c«se 'A': case 'a1: /¦ ¦/ case f\t} i case 'v': /* Выбор алгоритма фильтрации */ puts ("\n\t Выберите алгоритм фильтрации"); puts ("\t 1. XIFNES * медианный фильтр"); puts ("\t 2. KIFMHS - медианный фильтр"); puts ("\t 3. XIFMBS - медианный фильтр для бинарного поля"); puts ("\t РЕКУРСИВНЫЕ ФИЛЬТРЫ 1-го РОДА"); puts ("U 4. XIFMES - медианный фильтр");
5.9. Программы нелинейных фильтров 223 puts ("\t 5. XIFMBS - медианный фильтр для бинарного поля"), IDVIIT ("Ваи выбор?", tnueal, 1,ALGMAX); if ( go-'G'; go '*' ) goto LgQ; if (repeat) break; case 'M': case 'и': /¦ ———————--—————« /¦ Выбор маски IIMMFU ( Mask, LEMWIK, *Mi,tMj ); ¦/ go-'G'; if (repeat) break; Фильтрация исходного поля case 'G': case 'g': /¦ — LgG: /¦ switch (nueal) { case 1: KIFMES(Ieage,Ni,Nj, Mask,Mi,Mj, JeOut); break; case 2: KIFMHS(Ieage,Mi,Hj, Mask,Mi,Mj, IsOut); break; case 3: XIFMBS(Ieage,Ii,»j, Mask,Mi,Mj, IsOut); break; case 4: XIFMES(Ieage,Ni,Mj, Mask,Mi,Mj, leage); break; case 5: KIFMBS(Ieage,Mi,Mj, Mask,Mi,Mj, leage); break; default: puts ("Где-то вы напортачили puts("Профильтрованное поле"); if (nueaK-3) KIOTC (IeOut,»i,»j); else XIOTC (leage,Ri,Mj); break; PAUSE. if (cfi«»'Y> ** XDVYES(anHcaTb результаты в $*ял?", кстт) ) KIMMDW (Fout,8,Mask,Mi,Mj, 3, 1); if (nuual<«»3) XIOFPG (Fout,Q,I»0ut, »i,Ij, l,Ii, l,Ij, 2,79,60); else XIOFPG (Fout,0,luage, Ii,»j, l,»i, l,»j, 2,79,60); fprintf(Fout,"\nPHC. S.Xd. %в\пЛлгорнтн Xs, окно XdxXdAn", NumExp++,"Результаты фильтрации.", algorithe[nueal],Mi,Mj); > if (nueal>3) { puts("Исходное поле изменено рекурсивным фильтром"); go»'I'; } repeat¦¦; bre ak; case 'E': case 'e': /¦ */ goto END; default: puts ("Такой альтернативы нет"); goto BEGIW; EID: free (leage); free (IiiOut); if ( cfi »» >Y» ) { fprintf(Fout,"\n+\n"); fclose(Fout); } EID. Подпрограммы KIFGRP и KIFGAP, осуществляющие фильтра- фильтрацию по Робертсу, показаны на листинге 5.24. Обратите внима- внимание — перед обработкой выходной массив обнуляется (если толь- только подпрограмма не вызвана как рекурсивный фильтр); поэтому
224 Глава 5. Локальная фильтрация изображения необработанные края профильтрованного изображения всегда со- содержат нули. Здесь это полосы по правому и нижнему краям поля шириной в один пиксель. Результаты обработки этими программами тестового поля пен казаны на рис. 5.17. Листинг 5.24. Функции KIFGRP и KIFGAP finclude <aath,2t> •include <»tdio.h> iinclude "IMAGE.H" /* ФАЙЛ — KIFGRP.С */ void KIFGRP (Ieage,Ii,Ij, I»0ut) /* P-схема */ SHOW !¦&?•[], int /¦ Фильтрация скользящий окном - яа плоскости ¦/ /¦ Нелинейный фильтр по Робертсу, окно 2x2 элемента ¦/ /¦ Iaage - исходное изображение ¦/ /¦ Ii, Ij - разимры исходного и врофжльтровыгаого полей ¦/ /¦ ImOut - профжльтроважное поле (края не обр«.6атнв*»тс*) ¦/ /¦ . ¦/ /¦ Результат обработка залнсыв&етс* в тот элемент ¦/ /¦ поля IeOut, который яакрывает точка окяа A,1). */ COUIT ко, ks; int io,jo; /* текущее положение центра, окна. ¦/ int u,v; if (Iaage!«IaOut> { for (ko«Ii*Ij; ko>0; ) IaOut[—ko] » 0; } /* перенещеияе окаа */ for (ke«0, io»i; io<Wi; io-M>t for Cko»ke, jo«i; jo<Ij; jo-м-, u « Iaage С ko 3 - Iaage [ ko+Sj+1 ]; т * Image [ ko+1 ] - Image [ ko+Ij ]; Ie0ut[ko] « eqrt ( (float)u*u + (float)v*v ); void KIFGAP (Iuage.Ii.Ij, XeOut) /¦ Р-схема ¦/ SHOW Ieage[], U0ut[]; int Hi,Rj; /* Фильтрация скользящим окном - на плоскости */
5.9. Программы нелинейных фильтров 225 /* Нелинейный фильтр по Робертсу, ускоренный, окно 2x2 */ /„ «/ { C0UIT ко, же; int io,jo, u,v; if (Ieage'-IeOut) { for (ko»Ki*Mj; ko>0; ) IeOutO-ko] » 0; } /* перемещение окна */ for (ke«0, io»l; io<Ii; io**, ke* for (ko*xe, jo»i; jo<Ij; jo**, ko-м-) u * Iaage [ ko ] - Iaage [ ko+Nj+1 ]; v * Iaage [ ko+1 ] - Iaage [ ko+Nj ]; I»0ut[ko] * abs(u) + abs(v); Подпрограммы KIFGSP и KIFGTP (листинг 5.25) осуществля- осуществляют фильтрацию изображений по Собелу. Края выходного поля (шириной в один пиксель) остаются необработанными и заполне- заполнены нулями. Результаты обработки этими программами тестового поля по- показаны на рис. 5.18. Листинг 5.25, Функции KIFGSP и KIFGTP #include <»ath.h> finclude <etdio.h> tinclude "IMAGE.H" /¦ ФАЙЛ — KIFGSP.С ¦/ Ўold KIFGSP (lB&ge,Hi,fj, IaOut) /* P-схема ¦/ SHOW Ia&ge[], I»0utG; int li.Ij; /« . */ /* Фнльтр&цмя скользящим окном - я& плоскости */ /* Нелинейный фильтр по Собелу, окно: 3x3 элемента */ /* Ia&ge - исходное изображение */ /* Mi, Rj - размеры исходного н профильтрованного полей */ /* IaOut - профильтрованное поле (края не обрабатывается) */ /«, */ /* Результат обработки записывается в тот элемент */ /* поля I»0ut, который накрывает точка окна B,2). */ /« «/ { СОШГТ ко, кв; int io,jo; /* текущее положение центра окна */ int x,y;
226 Глава 5. Локальная фильтрация изображения if (Ii*ag«!«Ii*Out) { for (ko«»i«»j; ko>0; ) IuOut[—ko] « 0; } /* перемещение окна */ for (ke-Hj+1, io-2; io<»i; io++, ks+-Kj) for (ko«ks, jo*2; jo<»j; jo**, ко**) x * leage [ko-Kj+1] + 2«Iisage [ko+l] ¦ Ietge [ko+Kj+1] - letge Cko-»j-l] - 2«Iisage [ko-1] - letge [ko-Hfj-l]; у * leage [ko-Hj-1] ¦ 2«Ie&ge [ko-Rj] + la&ge [ko-Ij+1] - Iisage [ko+Ij-l] - 2«I*age [ko+Ij] - le&ge [ko-Hfj+i] ; Ia0ut[ko] » sqrt ( (float)x*x + (flo&t)y*y ); Ўoid KIFGTP (luage^Ii^j, IiOut) /¦ Р-схеил ¦/ SHOW I»&ge[], Ie0ut[]; int Ni,Nj; /* Фнльтр&цжя скользящим окном - я& плоскости */ /* Нелинейный фильтр по Собелу, ускоренный */ /* р&змер окна: 3x3 элемента */ C0UIT ко, кв; int io,jo, x,y; if (Iu&ge!«IisOut) { for (ko«»i«Ij; ko>0; ) Iu0ut[—ко] « 0; } /* перемещение окна */ for (ke-Kj+1, io«2; io<»i; io+*, ks* for (ko*ke, jo*2; jo<»j; jo**f ko++) x - le&ge [ko-Kj+1] 4- 2«Ie&ge [ko+l] 4- le&ge [ko+Kj+l] - le&ge [ko-»j-l] - 2«Iis&ge [ko-1] - leage [ko+Ij-l]; у » leage [ko-»j-l] ¦ 2«Iisage [ko-»j] ¦ le&ge [ko-Ij+l] - Iisage [ko+»j-l] - 2«Iis&ge [ko+Ij] - le&ge [ko+Ij-H] ; Ie0ut[ko] « &bs(x) + &bs(y); Подпрограммы KIFHUP и KIFHYS (листинг 5.26) выявляют существующие на изображении локальные максимумы (вершины) Алгоритм работы описан в предыдущем пункте. Подпрограмма KIFHUP реализует данный алгоритм с исполь- использованием Р-схемы перемещения окна; размер окна фиксирован и составляет 3x3 элемента. Подпрограмма KIFHYS построена на базе 5-схемы; окно может иметь произвольный размер.
5.9. Программы нелинейных фильтров 227 На рис. 5.19 приведены результаты выявления вершин на псев- псевдослучайном поле (рис. 1.6) для двух различных размеров окна. Листинг 5.26. Функции KIFHUP и KIFHYS finclude <»tdio.h> •include "IHAOB.H" /* ФАЙЛ — KIFHUP.С ¦/ roid KIFHUP (Iatg«,Ii,lj, IbOuO /¦ P-схема */ SHOW Ieage[], IiOutG; int Ii,Ij; /¦ ¦/ /* Фильтрация скользящим окном - на. плоскости */ /* Выявление вершин; окно: 3x3 элемента */ /* Вершины сохраняет свои значения, фон - нулевой. */ /* Iaage - исходное изображение */ /* Hi, Wj - размеры исходного и профильтрованного полей */ /* laOut - профильтрованное поле (края не обрабатывается) */ /«/ COURT ко» кв; int io,jo; /* текущее положение центра окна */ SHOV рр; /* Очистка - для нерекурсивного фильтра */ if ( Iaage !¦ IaOut ) { for (ko»Mi«Mj; ko>0; ) IiOut[—ko] « IODATA; } /* перемещение окна */ for (ke»Ij+l, io»2; io<Mi; io-м-, ks+*Mj) for (ko»ke, jo»2; jo<Ij; jo++, ko++) < pp ¦ Iaage [ko]; IaOut[ko] * (pp >• iBage [ko-Mj-1] fefe pp >• Iaage [ko-Mj] tk pp >• Iaage [ko-Ij+1] tt pp >* Iaage [ko+1] kk pp >• Iaage [ko+Mj+1] kk pp >« Iaage [ko+Ij] kk pp >¦ Iiage [ko+Hj-1] kk pp >¦ Ieage [ko-1] ) ? pp : 0 ; void KIFHYS (lBage,Ni,Kj, Mp,Mq, IsOut) /¦ S-схема ¦/ SHOW lBage[]f IiOut[]; int Ii,Rj,Mp,Mq; /¦ ф/ /* Фильтрация скользящим окном - на плоскости */
228 Глава 5. Локальная фильтрация изображения /* Выявление вершин. Вершины сохраняет своя значения, */ /* остальные элементы поля обнуляется */ /* Центр окна пробегает все поле, перемещаясь построчно. */ /* Окно MOIET выходить за пределы поля, */ /* во обрабатывается только часть окна, лежащая на поле. */ /* Результат обработки записывается в тот элемент ¦/ /* поля I«0ut, который накрывает точка окна (q«,q«). */ /* Iaage - исходное изображение */ /* Hi, Nj - размеры исходного и профильтрованного полей */ /* Мр, Mq - размеры окна */ /* I«0ut - профильтрованное поле */ /¦ */ { int register p,q; /* индексы окна (строка, столбец) */ int pl,p2, ql,q2; /* границы изменения индексов окна */ COURT ks; /* то же, в координатах поля */ int io,jo; /* текущее положение центра окна. */ C0UIT ко; /* текущее положение центра окна. ¦/ C0UIT к; /* текущий элемент поля */ int p«*(Mp+l)/2, qe»(Mq+l)/2; /* условный центр окна ¦/ SHOW pp; for (ko»0,io»l; io<»»i; io++) { pi » 1+pe-io; if ( pi < 1 ) pi » 1; p2 » Ii+pe-io; if ( p2 >Mp ) p2 » Mp; for (jo»l; jo<»Ij; jo++, ko++) { pp ¦ I«age [ko] ; ql » 1+qa-jo; if ( ql < 1 ) ql » 1; q2 » Ij+qe-jo; if ( q2 >Hq ) q2 » Mq; ImOut [ ko ] » 0; ks ¦ MAR!L(io-pe+pl,jo- for (p»pl; p<mp2; p++, ke+»Mj) for (k*ks, q»ql; q<mq2; q++, if ( pp < iB&geCk] > goto LocOut; I«0ut [ ko ] ¦ pp; LocOut:; Фильтрацию по Робертсу и по Собелу, а также выявление вер- вершин можно наблюдать с помощью тестовой программы KIFG5 (ли- (листинг 5.27). Эта программа аналогична по своему устройству про-
5.9. Программы нелинейных фильтров 229 грамме K1F03F, описанной в разд. 5.7, но не содержит операторов записи результатов в файл. Листинг 5.27. Тестовая программа KIFG5 •include <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIFG5.C ¦/ •include "KDV.H" /* Фильтрация изображений */ /* скользящим окном */ •define ALGHAX 6 char ^algorithm С ALGMAX+1 3 » { ||Н, "KIFGRP", "KIFGAP", "KIFGSP", "KIFGTP", "KIFHUP", "KIFHYS" }; void mainO { SHOW «Image, *ImOut; int Ni*16, Nj«20, Mi*3, Mj«3, lv»7; int algorsl; COUNT need; char repeat*0, go**!'; puts ("\n\n\tKIFGS - Фильтрация изображений скользящим окном."); puts ("\1;\гНелин8Йние фильтры"); BEGIN: if < repeat ) { put» (""); puts ("\t\t\tCneKTp возможностей:"); puts ("\t I - выбрать поле"); puts ("\t A - выбрать алгоритм"); puts ("\t G - фильтровать"); puts ("\t V - выбрать алгоритм И фильтровать"); puts ("\t E - завершить"); XDVCHR ("Что делать? <I/A/Q/V/E>H, *go); switch ( go ) { case 'I': case 'i*: /¦ */ /* Распределение памяти и создание поля */ if ( repeat ) { puts(cBo6oflHM память"); free (Image); free (ImOut); }
230 Глава 5. Локальная фильтрация изображения KDVIIT ("Высота, поля, строк Hi", Mi, 0,512); KDVIIT ("ширина поля, столбцов Hj", *Hj, 0,512); KIMERR ("XIFG5:", Hi.Hj); /¦ Контроль размеров ¦/ need * Mi * Hj; puts ("Выделяй память"); if (!(Ieage»ealloc(need))) { puts("Нет места для Image!"); STOP. } if (!(ImOut»malloc(need))) { puts ("Нет места для I«0ut!"); STOP. } if ( KIMGAU (Image, Hi,Hj, *1т) >» 3) { if (Ii>8 ** Hj>10) { Image[S*Hj-5] » It; Image[(Hi-4)*Hj-3] » It; Image[(Hi-4)*Hj-4] - It; Image[(Hi-4)*Hj-5] - Iv; Image[(Hi-4)*Hj-6] » It; > > puts(" Исходное изображение"); KIOTC(Image,Hi,Hj); PAUSE go*'A'; if ( repeat ) break; case 'A': case 'a': /* */ case 'IP: case 'v': /* Выбор алгоритма фильтрации */ puts (""); puts ("\t Выберите алгоритм фильтрация:"); puts ("\t 1. XIFGRP - по Робертсу"); puts ("\t 2. XIFGAP - по Робертсу, ускоренный"); put» ("\t 3. XIFGSP - по Собелу"); puts ("\t 4. XIFGTP - по Собелу, ускоренный"); puts ("\t Б. KIFHUP - вершины в окне 3x3"); puts ("\t 6. XIFHYS - вершины в заданном окне"); KDVIHT ("Ван выбор?", «algor, l.ALGMAX); if ( go—'V II go—'v' ) goto LgG; go*'G'; if ( repeat ) break; case 'G>: case 'g': /* */ LgG: /* Фильтрация исходного поля */ switch (algor) { case 1: XIFGRP (Image,Ii,Ij, leOut); break; case 2: XIFGAP (Image,HitMj, I»0ut); break; case 3: XIFGSP (Image,Ri,Mj, ImOut); break; case 4: XIFGTP (I«age,Hi,Hj, I»0ut); break; case 5: KIFHUP (Image,Hi,Hj, ImOut); break; case 6: КIMMAT (Hi*Hj/4, *Mi,*Mj); XIFHYS (Image,Hi,Hj, Mi,Mj, ImOut); break; default: puts ("Где-то вы напортачили ..."); break; > puts("Профильтрованное поле"); KIOTO (ImOut,Hi,Hj); PAUSE. if (go»»>G> || go—'g') go*'A'; repeat¦¦; break;
5.10. Рекурсивные нелинейные фильтры 231 case >Е>: саве 'в»: /«¦ ¦/ goto EID; default: puts ("Такой альтернативы нет"); > goto BEGII; END: free (Image); free (IeOut); EHD. 5.10. Рекурсивные нелинейные фильтры Все сказанное выше о линейной рекурсивной фильтрации справед- справедливо и для нелинейной рекурсивной. Так, известны рекурсивные медианные и процентильные филь- фильтры первого рода. Они реализуются с помощью описанных в разд. 5.9 подпрограмм KIFMES, KIFMHS и KIFMES, для чего при обращении к ним следует указать один и тот же массив как для входного изображения, так и для выходного. Программы фильтрации по Робертсу и Собелу также могут быть использованы в качестве рекурсивных фильтров 1-го рода, однако авторы не встречали упоминания о рекурсивных версиях этих фильтров в доступной литературе. Подпрограмма KIFREQ (листинг 5.28) превращает простые фильтры в рекурсивные 2-го рода. Эта подпрограмма исполь- использует более изящное решение по сравнению с KIFORS (листинг 5.17), но требует дополнительных ресурсов памяти для хранения промежуточного поля. С помощью подпрограммы KIFREQ лю- любой фильтр, имеющий те же 7 параметров, что и подпрограммы KIFOLS, KIFMES и т. п., превращается в рекурсивный фильтр 2- го рода. Подпрограмма KIFREQ имеет 8 параметров, первые 7 из которых совпадают с параметрами вышеназванных функций, а последний является указателем на используемую подпрограмму фильтрации. При обращении к KIFREQ надо помнить, что исходное поле (в массиве Image) сохраняется, а результат помещается в выходное поле ImOut. Напомним, что в подпрограмме KIFORS было сде- сделано наоборот, т. е. профильтрованное поле помещалось на место исходного, а в массиве ImOut оставалась копия исходного поля. Листинг 5.28. Функция KIFREQ tinelude <stdio.h> «include "IMAGE.H" /* ФАЙЛ — KIFREQ.С */
232 Глава 5. Локальная фильтрация изображения Ўoid KIFREQ <Imtge,Ii,Ij, window,Ир,Mq, I «Out, KIFO) SHOW Ieige[], I»0ut[]; char window D; int Ii,Ij,Mp,Mq; Ўoid <*IXF0)O; /» ¦/ /¦ Фяльтрмщя скользящим окном - hi плоскостн */ /* ОБОБЩЕННЫЙ РЕКУРСИВНЫЙ ФИЛЬТР 2-го РОДА */ /* iB&g* " исходное изображение ¦/ /¦ Mi, Ij - размеры исходного к профильтрованного полей */ /* vindov - весовая функция окна */ /* Ир, Hq - размеры окна */ /* 1*0tit - профильтрованное поле (сначала очнщаятся) ¦/ /* KIFO - подпрограмма простого фильтра ¦/ int k; SHOV *; СОШГТ need ¦ Ii ¦ Ij; if (!(IeBuf«Balloc(need))> { putsO'HeT места для ImBuf!"); STOP. } if < I«age « I«Out ) { puts ("KIFREQ: некорректное обращение"); return; } for (k«need; k>0; ) { k—; I«Buf[k] « IeageCk]; } (¦KIFO) (Ieage,Ii,Ij, window,Ир,Mq, IeOut); /¦ Простой ¦/ («KIFO) (IeBuf,Ii,Ij, window,Hp,Hq, IaBuf); /¦ Рекурсивный */ for (k«need; k>0; ) { k—; IeOut[k] ¦ (iBOut[k]-HeBuf[k])/2; > free (IaBuf); Для наблюдения за работой рекурсивных фильтров 2-го ро- рода служит тестовая программа KIFR6 (листинг 5.29), устроенная аналогично KIFG5, описанной в предыдущем разделе. Функции, выступающие в роли параметров подпрограммы KIFREQ, должны быть объявлены как внешние оператором extern. Листинг 5.29. Тестовая программа KIFR6 finclude <stdio.h> •include "IMAGE.H" /¦ ФАЙЛ — KIFR6.C ¦/ ¦include "KDV.H" /* Фнльтр&цня изображений */ /¦ скользящий окном */
5.10. Рекурсивные нелинейные фильтры 233 •define ALGMAX 4 /* Количество алгоритмов в мена */ ¦define LEIWII 64 /¦ Маке, число uieMe*TOB маски ¦/ char Mask [ LEIWII ]; char «algorithm С ALGMAX+1 ] ¦ { "", "KIFOLS", "KIFMES", "KIFNHS", "KIFMBS" }; extern void KIFOLSO, KIFMESO, KIFMHSO, KIFMBSO; void aainO { SHOW *lBage, «IaOut; int Ii»16, *j«20, Mi»3, Mj»3, lv«7; int algor»l; COUIT need; char repeat»0, go« * i'; puts ("\n\n\tKIFE6 - Фильтрация изображении скользящим окном."); puts (n\t\tPexypcHBHue фильтры 2-го рода"); BEGIN: if ( repeat ) { put» (""); puts ("\t\t\tCneKTp возможностей:"); puts ("\t I - выбрать поле"); puts ("\t A - выбрать алгоритм"); puts ("\t M - выбрать м&ску"); puts ("\t G - фильтровать"); puts ("\t W - выбрать алгоритм Н фильтровать"); puts ("\t E - завершить"); KDVCHR ("Что делать? <I/A/M/G/W/E>", *go); switch ( go ) { case 'I': case 'i': /¦ —— —-—— ¦/ /* Распределение памяти и создание поля */ if ( repeat ) { puts("Освободим память"); free (Isage); free (IaOut); } KDVIIT ("Высота поля, строк li", *Ii, 0,812); KDVIIT (И1ирина поля, столбцов Mj", tlj, 0,512); KIMERR ("KIFG5:", Ii,Ij); /¦ Контроль размеров */ need ¦ li • Ij; puts ("Выделим память"); if (»(lBage*Balloc(need))) < putsC'HeT места для Ieage!"); STOP. } if (!(lBOut«Balloc(need))) { putsC'HeT места для IeOut!"); STOP. }
234 Глава 5. Локальная фильтрация изображения if ( KIHGAU (Iaage, { if (Ii>8 ftft Ij>10) *1т) « 3) { lBage[8*Ij-6] - It; lBage[(Ii-4)«Ij-3] * It; lBage[(Ii-4)*Ij-4] - It; lBage[(Ii-4)*Ij-5] - It; lBage[(Ii-4)*Ij-6] - It; puts(" 1сходиое изображение"); KIOTC(lBage,Ii,Ij); PAUSE go*>A'; if ( repeat ) break; case *A': case 'a': /¦ case *Ы* : case 'w': /* Выбор алгоритма фильтрация puts (""); puts ("\t Выберите алгоритм фильтрации:"); puts ("\t 1. KIFOLS - линейный фильтр"); puts ("\t 2. KIFNES - медианный фильтр"); puts ("\t 3. KIFMHS - медианный фильтр"); puts ("\t 4. KIFMBS - медианный фильтр для бинарного поля"); KDVIIT ("Baa выбор?", fcalgor, 1,ALGMAX); if ( go**'*!' || go**'»' ) goto LgG; go*'G if ( repeat ) break; ¦/ ¦/ case 'M': case 'в': /¦ — /¦ KIMMFU (Mask, LEKWIN, go*'G'; Выбор маски if ( repeat ) break; ¦/ case 'G': case 'g': /¦ LgG: /* switch (algor) { case 1: KIFREQ(I«age,Ii,Ij,Mask,Mi,Mj,I«0ut case 2: KIFREQ(I«age,Ii,Ij,Mask,Mi,Mj,I«Out Фильтрация исходного поля KIFOLS) KIFHES) ¦/ */ break; break; case 3: KIFREQ(I«age,Ii,Ij,Mask,Hi,Mj,IeOut, KIFMHS); break; case 4: KIFREQ(Ieage,Mi,fj,Mask,Mi,Mj,IeOut, KIFMBS); break; default: puts ("Где-то вы напортачили ..."); break; } puts("Профильтрованное поле"); KIOTO (IaOut,Ki,Kj); PAUSE. if (go**'G' go*'A'; repeat++; break; case 'E': case 'e': /¦ default: puts ("Такой альтернативы нет"); ¦/ goto END; goto BEGII;
5.10. Рекурсивные нелинейные фильтры EID: free (Image); free (ImOut); EID. 235 На листинге 5.30 показан файл KIF.H, содержащий прототипы функций локальной фильтрации, описанных в данной главе. Этот файл не требуется явно подключать к каждой программе операто- оператором include, так как он подключен к головному файлу IMAGE.H. Листинг 5.30. Головной файл KIF.H /¦ ФАЙЛ — KIF.H ¦/ void KIFGAP (SHOW Image G, int Hi, int Hj, SHOW ImOutd); void KIFGRP (SHOW Image [], int Mi, int Nj, SHOW ImOutG); void KIFGSP (SHOW Image[], int Mi, int Ij, SHOW ImOutG); void KIFGTP (SHOW Image[], int Mi, int Mj, SHOW ImOutG); void KIFHUP (SHOW Image G, int Hi, int Hj, SHOW ImOutG); void KIFHYS (SHOW Ieage[], int, int, int, int, SHOW ImOutG); void KIFILP (SHOW Image[], int, int, int, int, SHOW InOutG); void KIFILS (SHOW Image [], int, int, int, int, SHOW ImOutG); void KIFILT (SHOW Image [], int, int, int, int, SHOW Im0ut[]); void KIFMBS (SHOW Image[], int, int, char MaskG, int, int, SHOW ImOutG); void KIFMES (SHOW Image[], int, int, char HaskG, int, int, SHOW Im0ut[]); void KIFHHS (SHOW Image[], int, int, char Hask[], int, int, SHOW ImOutG); void KIFOLP (SHOW Image G, int, int, char M&skG, int, int, SHOW ImOutG); void KIFOLS (SHOW Image[], int, int, char Nask[], int, int, SHOW ImOutG); void KIFOLT (SHOW Image[], int, int, char MaskG, int, int, SHOW IniOutG); void KIFORS (SHOW Image G» int, int, char MaskG, int, int, SHOW ImOutG);
ПРИЛОЖЕНИЕ Указатель программ и головных файлов (в скобках указан номер листинга, тест-программы помечены звездочкой) KDVCHR KDVFLO KDVINT KDVYES KHISAN KHISTA KHISTO KHISTW KIAFFI KIAFFS KIAFOB KIAF1* KIAF2* KIAPAR KIAPNC KIAPPD KIATVF KIATVS KIBCDM KIBCKO KIBDEK KIBKOD KIBORD KIBOUT KIBL1* KIBL2* KIBL3* KIFGAP KIFGRP KIFGSP E.19) D.3) - A.16) - A.17) - C.6) - C.7) - C.7) - C.7) - D,1) - D.1) - D.1) - D.4) - D.4) - D.2) - D.2) - D.2) - D.2) - D.2) - A.23) - A.23) - A.23) — A.23) - A.23) - A.23) - A.24) - A.25) - A.26) - E.24) - E.24) — E.25) — KIFGTP E.25) диалоговый ввод символа диалоговый ввод плавающей переменной диалоговый ввод целой переменной диалоговый выбор из двух альтернатив вычисление статистики изображения по гисто- гистограмме алфавитно-цифровой вывод гистограммы то же с относительной оцифровкой вывод гистограммы с диалоговым управлением аффинное преобразование ноля по ближайше- ближайшему дискрета- то лее с разбиением на подклетки расчет параметров обратного преобразования аффинное преобразование поля с диалоговым управлением то же с выводом результатов в файл диалоговый ввод параметров и добавление в це- цепочку расчет положения неподвижной точки диалоговый ввод параметров преобразования подобия вывод параметров аффинного преобразования в файл то же на экран бинарное квантование блока с сохранением М и D бинарное квантование блока по минимуму С. К. О. декодирование упакованного изображения блочное кодирование изображения и упаковка чтение упакованного ноля с диска и декодиро- декодирование кодирование и запись на диск с диалоговым управлением - блочное кодирование поля и просмотр на эк- экране — блочное кодирование; просмотри вывод в файл блочное кодирование: сохранение и восстано- восстановление нелинейный фильтр по Робертсу, ускоренный нелинейный фильтр по Робертсу нелинейный фильтр по Собелу нелинейный фильтр по Собелу, ускоренный
Приложение 237 KIFG5* KIFHUP KIFHYS KIFILP KIFlLS KIFILT KIFIl* KIFMBS KIFMES KIFMHS KIFM4F* K1FOLP KIFOLS KIFOLT KIFORS KIFO2F* KIFO3F* KIFREQ KIFR6* K1GLIN KIGPNT KIMAGL KIMEMO KIMERR KIMERS KIMGAU KIMGCR KIMGDI KIMGSQ KIMGTP KIMGUR KIMGWF KIMG4* KIMHIS KIMHFR KIMHMA КШН1* KIMH2* KIMMAT KIMMDW KIMMD KIMMFB KIMMFO KIMMFU KIMMIN KIMMLI KIMMSM 5.27) E.26) E.26) E.1) E.2) E.3) E.4) E.22) E.20) E.21) E.23) E.7) E.8) E.9) E.17) E.10) E.18) E.28) E.29) A.4) A.4) A.1) E.5) A.10) A.10) A.8) A.3) A.2) A.5) A.4) A.6) A,7) A.9) C.4) C.4) C.5) C.8) C.9) E.6) E.11) E.11) E.12) E.13) E.13) E.14) E.12) C.9) нелинейные фильтры: Роберте, Собел и т. д. выявление вершин в окне 3x3 выявление вершин в заданном окне линейный низкочастотный фильтр линейный низкочастотный фильтр линейный низкочастотный фильтр линейные безмасочные фильтры медианный фильтр для бинарного поля медианный фильтр (через вариац. ряд) медианный фильтр (через гистограмму) нелинейные фильтры — медианные универсальный линейный фильтр универсальный линейный фильтр универсальный линейный фильтр рекурсивный линейный фильтр 2-го рода линейные фильтры с произвольной маской линейные рекурсивные фильтры обобщенный рекурсивный фильтр 2-го рода нелинейные рекурсивные фильтры прямая по алгоритму Брезенхема точка определения глобальных переменных ввод размеров поля и распределение памяти контроль размеров поля контроль размеров фрагмента поля выбор и создание тестового изображения концентрическое заполнение поля диагональное заполнение поля черный квадрат тестовая картинка типа «рама» поле некоррелированных случайных чисел поле коррелированных случайных чисел создание и просмотр случайного поля получение гистограммы изображения то же для фрагмента изображения то же по маске статистический анализ поля или фрагмента статистический анализ фрагмента по маске выбор размеров окна числовой вывод маски в файл числовой вывод маски на экран создание бинарной маски выбор стандартной маски 3x3 выбор размеров и создание маски диалоговая коррекция элементов маски прямая по алгоритму Врезенхема создание прямоугольной маски
238 Приложение KIMMO* KIMSTA KIMSTF KINBIN KINBSV KINSAV KINSIN KINB2* KINS3* KIOFAX KIOFCW KIOFDW KIOFEY KIOFPG KIOTC* KIOTD* KIOFO* KIOTl* KIPGDE KIPGUM KIPGO* KIPICL KIPIMA KIPIMD KIPIOB KIPION KIPIl* KIPI2* KIPI3* KIPJAR KOSMOM KOSPTF KOSPUF KDV.H KIAFF.H KIB.H KIF.H KIMAG.H KIMM.H KIP.H KOST.H — формирование и просмотр масок — статистика изображения, непосредственно — то лее для фрагмента изображения — чтение двоичного поля из дискового файла — двоичная запись поля на диск — символьная запись поля на диск — чтение символьного поля из дискового файла — двоичный ввод/вывод поля в файл — символьный ввод/вывод поля в файл — оцифровка горизонтальной оси — символьный вывод фрагмента поля — числовой вывод фрагмента поля — вывод фрагмента поля с диалоговым управле- управлением AЛЗ) — вывод фрагмента поля по страницам A.13) — вывод поля на экран по страницам — символь- символьный A.13) — вывод поля на экран по страницам — числовой A.15) — вывод поля в файл A.14) — вывод поля по страницам на экран B.3) — коррекция положения и размеров поля B.3) — формирование клише для тонового вывода B.5) — просмотр матриц клише B.2) — выбор цвета B.1) — тоновый вывод поля на экран B.6) — то же с возможностью поворота B.9) — вывод поля на экран в 4 условных цветах B.10) — вывод поля на экран в 16 условных цветах B.4) — тоновый вывод поля на экран B.7) — то лее с возможностью поворота B.11) — вывод поля на экран в условных цветах B.8) — получение функции преобразования яркости C.2) — пересчет начальных моментов в центральные C.3) — вывод статистики на экран или в файл C.3) — то же в столбец A.29) — прототипы функций D.6) — прототипы функций A.28) — прототипы функций E.30) — прототипы функций A.27) — определения и подстановки E.15) — прототипы функций B.12) — прототипы функций (ЗЛО) — прототипы функций
ЛИТЕРАТУРА [1] Павлидис Т. Алгоритмы машинной графики и обработки изо- изображений. Пер. с англ. — М.: Радио и связь, 1986. — 400 с. [2] Фурман Я. А., Юрьев А. Н., Яншин В. В. Цифровые методы обработки и распознавания бинарных изображений. — Крас- Красноярск: Изд-во Краснояр. ун-та, 1992. — 248 с. [3] Фоли Дж., вэн Дэм А. Основы интерактивной машинной гра- графики. В 2-х книгах. Пер. с англ. — М.: Мир, 1985. — Кн. 1 — 368 с, кн. 2 — 368 с. [4] Хирн Д., Бейкер М. Микрокомпьютерная графика. Пер. с англ. — М.: Мир, 1987. — 352 с. [5] Прэтт У. Цифровая обработка изображений. В 2-х книгах. Пер. с англ. — М.: Мир, 1982. — Кн. 1 — 312 с, кн. 2 — 480 с. [6] Дуда Р., Харт П. Распознавание образов и анализ сцен: Пер. с англ. — М.: Мир, 1976. — 512 с. [7] Розенфельд А. Распознавание и обработка изображений с помо- помощью вычислительных машин. Пер. с англ. — М.: Мир, 1972. — 232 с. [8] Фурман Я. А., Яншин В. В. Многошаговые процедуры приня- принятия решений. — Красноярск: Изд-во Краснояр. ун-та, 1989. — 296 с. [9] Чанг Ши Као. Принципы проектирования систем визуальной информации. Пер. с англ. — М.: Мир, 1992 (в печати). [10] Фигурнов В. Э. IBM PC для пользователя, 2-е изд., перераб. и доп. — М.: Финансы и статистика, Компьютер пресс, 1991. — 288 с. [11] Болски М. И. Язык программирования Си. Справочник. Пер. с англ. — М.: Радио и связь, 1988. — 96 с. [12] Уэйт М., Прата С, Мартин. Язык Си. Руководство для начина- начинающих. Пер. с англ. — М.: Мир, 1988. — 512 с. [13] Керниган В., Ритчи Д. Язык программирования Си. Пер. с англ. 2-е изд., перераб. и доп. — М.: Финансы и статистика, 1992. — 272 с. [14] Уинер. Язык Турбо Си. Пер. с англ. — М.: Мир, 1991. — 384 с. [15] Бошкин А. В., Дубнер П. Н. Работа в Турбо-Си. — М.: НИВФ «ЮКИС», УНЦ «ТРЭК», СП «ЛАНИТ», 1991. — 183 с. [16] Язык Си для профессионалов. — М.: ИВК-СОФТ, 1991. — 384 с. [17] Коутс Р., Влейминк И. Интерфейс «Человек — компьютер». Пер. с англ. — М.: Мир, 1990. — 504 с. [18] Горяинов В. Т., Журавлев А. Г., Тихонов В.И. Примеры и зада- задачи по статистической радиотехнике. — М.: Сов. радио, 1970. — 600 с. [19] Феллер В. Введение в теорию вероятностей и ее приложения. Том 1. Пер. с англ. — М.: Мир, 1984. — 528 с.
240 Л итература [20] Delp ?. J., Mitchel O. R. Image compression using block truncation coding. IEEE Trans. Commun., v. COM-27, #9, 1979, pp. 1335-1342. [21] Латышев В. В. Оптимизация усеченного блочного кодирования изображений. В сб. научн. трудов МАИ «Вопросы передачи, распределения и обработки информации в задачах испытаний летательных аппаратов». — М.: Изд-во МАИ, 1982. — 80 с. [22] Ramm A. G. Random Estimation Theory. — Harlow: Longman, 1990, 271 p. [23] Яншин В. В. Выделение подозрительных областей бинарной сцены по признаку наличия сгущений. Автоматика, 1990, К* 5. Изд. АН УССР, с. 33-38. [24] Яншин В. В. Алгоритмы селекции по площади бинарных изо- изображений и их математические модели. — Радиотехника и электроника, 1991, вып. 11, с. 2111-2115. Изд. АН СССР. [25] Пушкин А. В., Яншин В. В. Корреляционные функции выход- выходных сигналов медианных и процентильных фильтров бинар- бинарных изображений. — Сб. Вопросы радиоэлектроники, сер. ОВР, 1991, вып. 12, с. 147-155. [26] Крамер Г. Математические методы статистики. Пер. с англ. — М.: Мир, 1975. — 648 с. [27] Ярославский Л. П. Введение в цифровую обработку изображе- изображений. — М.: Сов. радио, 1979. — 312 с. [28] Файн В. С. Опознавание изображений (основы непрерывно- групповой теории и ее приложения). — М.: Наука, 1970. — 296 с. [29] Путятин Е. П., Аверин С. И. Обработка изображений в робо- робототехнике. — М.: Машиностроение, 1990. — 320 с. [30] Анисимов Б. В., Курганов В. Д., Злобин В. К. Распознавание и цифровая обработка изображений. — М.: Высш. шк., 1983. — 296 с. [31] Калинин Г. А. Быстрый алгоритм геометрических преобразо- преобразований изображений. В сб. научн. трудов МАИ «Вопросы пере- передачи, распределения и обработки информации в задачах испы- испытаний летательных аппаратов». — М.: Изд-во МАИ, 1982. — 80 с. [32] Калинин Г. А. Комплекс программ геометрической трансфор- трансформации изображений. В сб. Тезисы докладов. I Всесоюзная кон- конференция «Методы и средства обработки сложноструктури- сложноструктурированной семантически насыщенной графической информа- информации». — Горький, 1983. [33] Хемминг Р. В. Цифровые фильтры. Пер. с англ. — М.: Сов. радио, 1980. — 224 с. [34] Быстрые алгоритмы в цифровой обработке изображений. Преобразования и медианные фильтры. Хуанг Т. С, Эклунд Дж.-О., Нуссбаумер Г. Дж. и др. Пер. с англ. — М.: Радио и связь, 1984. — 224 с.
ОГЛАВЛЕНИЕ Предисловие 5 Введение 6 Язык Си для обработки изображений F) О технике работы с пакетом программ (8) О тестовых программах (9) 1. Представление и хранение изображений 10 1.1. Представление изображений в программах A0) 1.2. Формирова- Формирование тестовых изображений A5) 1.3. Символьный и числовой вывод B9) 1.4. Сохранение изображений на магнитном диске D2) 1.5. Сокращен- Сокращенное кодирование D9) 2. Визуализация изображений 67 2.1. Полутоновый вывод F7) 2.2. Программы полутонового вывода на экран F8) 2.3. Выравнивание гистограммы (87) 2.4. Вывод в условных цветах (89) 3. Элементарный анализ изображений 95 3.1. Задачи статистического анализа (95) 3.2. Числовые характеристи- характеристики изображений (96) 3.3. Подпрограммы статистического анализа изо- изображений A01) 3.4. Получение общих и локальных гистограмм A06) 3.5. Гистограммы по маске A10) 3.6. Анализ гистограмм A12) 3.7. Вы- Вывод гистограмм A14) 3.8. Тестовые программы A22) 4. Геометрические преобразования 129 4.1. Аффинное преобразование и его подгруппы A29) 4.2. Программы аффинного преобразования A31) 4.3. Ввод аффинных параметров A37) 4.4. Вспомогательные подпрограммы A44) 4.5. Тестовые программы A45) 5. Локальная фильтрация изображения 153 5.1. Принципы локальной фильтрации A53) 5.2. Замечания о движе- движении окна A55) 5.3. Базовый алгоритм локальной фильтрации A58) 5.4. Линейные фильтры A68) 5.5. Программы линейных фильтров A82) 5.6. Программы формирования масок A89) 5.7. Рекурсивные линейные фильтры A97) 5.8. Нелинейные фильтры B06) 5.9. Программы нели- нелинейных фильтров B14) 5.10. Рекурсивные нелинейные фильтры B31) Приложение. Указатель программ и головных файлов 236 Литература 239 По поводу приобретения дискеты с исходными программ обращайтесь по адресу: 127560, г. Москва, Калинину Григорию Але или Яншину Валерию Владимировичу, до вое