Текст
                    К.ЛИНДЛИ
К.ЛИНДЛИ
ПРАКТИЧЕСКАЯ ОБРАБОТКА ИЗОБРАЖЕНИЙ
НА ЯЗЫКЕ
ПРАКТИЧЕСКАЯ ЛI 1
ОБРАБОТКА I 1 Л
ИЗОБРАЖЕНИЙ I |И
НА ЯЗЫКЕ V Г I
Издательство «Мир»

ПРАКТИЧЕСКАЯ ОБРАБОТКА ИЗОБРАЖЕНИЙ НА ЯЗЫКЕ СИ
PRACTICAL IMAGE PROCESSING IN C • Acquisition • Manipulation • Storage Craig A. Lindley John Wiley & Sons, Inc. New York Chichester Brisbane Toronto Singapore
К.ЛИНДЛИ ПРАКТИЧЕСКАЯ ОБРАБОТКА ИЗОБРАЖЕНИЙ НА ЯЗЫКЕ СИ Перевод с английского А. А. Брюзгина Издательство «Мир» 1996
ББК 32.973 Л59 УДК 519.687 Линдли К. Л59 Практическая обработка изображений на языке Си: Пер. с англ. — М.: Мир, 1996. — 512 с., ил. ISBN 5-03-002992-3 В книге американского специалиста в области обработки изображений рассмотрены аппаратные средства цифрового видеопреобразователя, про- граммные средства вывода на экран, а также форматы и функции графи- ческих файлов, в том числе популярный формат TIFF. Представлены клас- сические методы обработки изображений, основные алгоритмы и программ- ные модули, позволяющие пользователю писать собственные прикладные программы для обработки изображений на языке Си. Для программистов и квалифицированных пользователей. ББК 32.973 Редакция литературы по информатике и новой технике Издание выпущено в свет при содействии Комитета РФ по печати ISBN 5-03-002992-3 (русск.) ISBN 0-471-53062-Х (англ.) © 1991 by John Wiley & Sons, Inc. All Rights Reserved. Authorized translation from English language edition published by John Wiley & Sons, Inc. © перевод на русский язык, Брюзгин А. А., 1996
Предисловие Методы получения и обработки изображений вначале использовались только в промышленности. Обработка изображений, полученных из космоса, как сред- ство исследования запасов полезных ископаемых на Земле, астрономия, метеоро- логия, автоматизированный контроль, робототехника — только часть примене- ний этих методов. Получение, обработка, анализ и вывод изображений на экран требовали до недавнего времени компьютеров и графических приборов, не до- ступных для частных лиц. Помимо аппаратных средств, обработка изображений требовала хорошей математической подготовки для понимания и применения сложных алгоритмов, с помощью которых обрабатывались изображения. Чтобы понять алгоритмы, приходилось изучать программы, которые были, как прави- ло, написаны на не комментируемом и достаточно запутанном языке Фортран. В настоящее время ситуация меняется. Уже сейчас обработка изображений с помощью персонального компьютера не только возможна, но и сулит успех и процветание. Чтобы использовать методы обработки изображений, теперь не обязательно работать на крупном предприятии. Обратите внимание на поток ре- кламы факсимильных аппаратов, видеопреобразователей, планшетных и ручных сканеров, цветных сканеров и других устройств ввода изображений, которые те- перь доступны каждому. Такая техника стала доступной, поскольку резко вы- рос уровень производительности персональных компьютеров и качество графи- ческих адаптеров высокого разрешения, а также из-за низкой стоимости аппа- ратуры для цифровых преобразователей. То, что раньше выполнялось только в лабораториях крупных корпораций, теперь может быть сделано с помощью пер- сонального компьютера. Хорошо известная пословица «лучше один раз увидеть, чем сто раз услышать» находит новое приложение в области автоматизации из- дательского процесса. Графика и воспроизведение изображений приобретают такое важное значе- ние в окружающем нас мире, что людей многих различных профессий теперь объединяет общая область деятельности. Инженеры-электронщики, художники- графики, видеотехники, дизайнеры и программисты в области компьютерной графики — все они испытали на себе революцию в видеотехнике. Поэтому эта книга будет им очень полезна. Цель книги как поддержки переворота в технологии воспроизведения изо- бражений — сделать общедоступными возможности получения и обработки изо- бражений. Представленное здесь устройство ввода для компьютеров семейства PC является самым дешевым изпвсех имеющихся цифровых преобразователей и может рассматриваться как шаг к достижению этой цели. Устройство позволя- ет импортировать неподвижные видеоизображения от недорогой видеокамеры в компьютер для вывода на экран монитора и/или для обработки. Проекту аппа- ратуры и монтажу этого устройства посвящена значительная часть книги. В части I книги после изложения теоретических обоснований аппаратных средств и проекта цифрового преобразователя рассматривается программное обеспечение, которое необходимо для получения изображения с различным раз-
6 Предисловие решением, и обсуждаются способы вывода изображения на экран монитора. Кро- ме того, в книге показано, как можно получать прекрасные цветные изображе- ния, используя монохромную видеокамеру. Обсуждение дополнено прикладны- ми программами, которые иллюстрируют представленные в книге идеи. Для де- монстрации возможностей работающего цифрового преобразователя приводятся полученные с его помощью изображения. Сами по себе оцифрованные изображения не имеют большой пользы. Чтобы стать полезными, они должны быть сначала приведены к такому виду, в котором их можно обрабатывать другими прикладными программами. Поэтому в книгу включена глава по графическим форматам PCX и TIFF, которые используются во многих прикладных программах для PC. Изображения, оцифрованные ви- деопреобразователем, могут быть записаны в любом из этих форматов. Затем их можно импортировать в программу Paintbrush или в программы электрон- ной верстки для включения в доклады, представления и рекламные проспекты. Графические форматы не только подробно обсуждаются, но и приводится код на языке Си для считывания и записи этих форматов1. По мере необходимости приводится также полная спецификация форматов. И наконец, представлена программа просмотра, которая может выводить на экран данные файлов изо- бражения в любом из этих форматов. Для преобразования изображений, полученных на экране монитора, в изо- бражения на бумаге требуются специальные методы, поэтому гл. 7 полностью посвящена распечатке изображений. Подробно обсуждаются фотографические методы и методы распечатки с помощью принтера, а также дается программа, которая позволит воспроизводить графические изображения на принтере, ис- пользуя популярные алгоритмы печати. Часть II посвящена классической обработке изображений. При этом ис- пользуется практический, а не строгий математический подход. Все основ- ные классы алгоритмов обработки изображений проиллюстрированы примера- ми программ на языке Си и изображениями, которые показывают влияние ал- горитма. Информация, представленная в этих главах, применяется в машин- ном зрении, автоматизированном контроле, улучшении изображений, робото- технике и т. д, Необходимые условия Для использования программ, приведенных в этой книге, необходимо иметь до- ступ к следующим средствам: a) Turbo С версии 1.5 или выше. Можно сделать также программу для рабо- ты с другими видами компиляторов Си. Например, подробно обсуждается использование Microsoft С. б) Макроассемблер типа MASM фирмы Microsoft или TASM фирмы Borland. в) IBM-совместимый персональный компьютер с графическим адаптером VGA, цветным монитором и принтерным портом. 1Код TIFF не приведен в самой книге из-за его большого размера.
Предисловие 7 Отметим, что цифровой преобразователь не приводится в этом списке как обязательное средство. Программы, представленные в книге, как правило, не зависят от входного устройства и не требуют использования цифрового преобра- зователя. Приведенные алгоритмы могут обрабатывать входные данные, полу- ченные с помощью сканеров, ловушек кадров и большинства других устройств ввода графической информации. Цифровой преобразователь является лишь од- ним из множества других источников входных графических данных. Дешевизна и простота этого устройства позволят самому широкому кругу людей участво- вать в развитии методов ввода и обработки изображений. Все программы были проверены на компьютерах IBM PS/2 моделей 60 и 70 с ОЗУ 1 Мбайт, графическим адаптером VGA, последовательным и параллельным портами. Программы ввода изображений для цифрового преобразователя будут работать на любом IBM-совместимом компьютере, начиная с оригинального 8088 4.77 МГц. Однако изображения должны выводиться на экран с помощью ада- птера VGA. Для чтения книги необходимо иметь общие знания по программированию на языке ассемблера для семейства процессоров Intel 80 х 86 и некоторый опыт в программировании на языке Си; книга ни в коей мере не претендует на роль учебника по этим языкам. Все программы хорошо документированы и долж- ны быть понятны даже для начинающего. Следует отметить, что при разработ- ке программ их производительность считалась более важным критерием, чем возможность их переноса. Даже для современных мощных PC обработка изо- бражений требует всех имеющихся вычислительных возможностей. Чем выше мощность компьютера, используемого для решения этой задачи, тем лучше. Это не означает, что программы не являются переносимыми. Наоборот, при необхо- димости программы можно будет перенести на любой компьютер семейства PC. Имеются сопроводительные диски, которые содержат все исходные программы, разработанные в книге, а также несколько изображений, которые вы можете просто посмотреть и при желании поэкспериментировать с ними. Итак, главная цель книги — ввести вас в завораживающий мир обработки изображений. Вы увидите, что обработка изображений не только имеет множе- ство практических применений, но также может быть очень увлекательным за- нятием. Я надеюсь, вы будете рады иметь свой цифровой видеопреобразователь и получите удовольствие, экспериментируя в обработке изображений. Помните, что одно оцифрованное изображение включает 32 000 слов (или 64 000 байт) компьютерной памяти. С наилучшими пожеланиями, Крэйг А. Линдли Манитоу Спрингс, Колорадо Ноябрь 1990 г.
Благодарности Эту книгу я посвящаю моей жене Геате Хаббард, которая проявила достаточное терпение, чтобы прочитать корректуру книги, и мудрость, чтобы не позволить мне сгореть при ее написании, а также моим родителям Меррил и Ниву Линд- ли, которые учили меня, что самомотивация является положительной чертой характера. Всем вам я очень благодарен. Особую благодарность я хотел бы выразить следующим частным лицам и компаниям, которые помогали в подготовке и издании книги: Диане Серра и Терри Хадсону из издательства John Wiley & Sons за помощь в опубликовании книги; Деннису и Кэти Стоктон за консультации по фотографическому оборудо- ванию; корпорации ZSoft за предоставление сигнального экземпляра своего от- личного программного продукта PC Paintbrush IV Plus, который помог отладить библиотеку TIFF, и за предоставление спецификаций формата PCX; корпорации Aldus за разрешение включить спецификацию TIFF 5.0; журналу «Gourmet» за разрешение использовать фотографию с обложки журнала. И наконец, Стиву Сайэрсиа за его электрическую схему. Все изображения в книге получены с фотографий, сделанных Геатой Хаббард и автором, или взяты из общедоступных источников. Последние использовались с соответствующего разрешения. К. А. Линдли
Часть I Проект цифрового видеопреобразователя Глава 1 Общие сведения В главе рассмотрены следующие вопросы: ♦ Ограничения программ, написанных для MSDOS. ♦ Архитектура процессоров Intel 80 х 86. ♦ Возможности и ограничения графических адаптеров PC. ♦ Палитра, цвет и режимы работы цветового регистра адаптеров EG А/VGA. ♦ Использование возможностей графических адаптеров VGA. ♦ Работа порта печатающего устройства. ♦ Работа с сегментами и моделями памяти. ♦ Распределение данных между программами на языках ассемблера и Си. Введение При изучении нового предмета, будь то обработка изображений, автодело или садоводство, какое-то время необходимо вначале затратить на понимание основ- ных идей, на которых построен этот предмет. Рассмотрение основ не всегда мо- жет быть интересно, но во многих случаях необходимо. В данной главе часть времени мы уделим обсуждению вопросов, непосредственно не связанных с по- лучением и обработкой изображений. Обсуждение сконцентрируем на основах, необходимых для полного понимания и применения методов создания изображе- ния, которые рассматриваются позже. Так как проект цифрового видеопреобра- зователя, представленный в книге, специфичен для IBM PC (или совместимых
10 Глава. 1. Общие сведения с ним компьютеров), значительная часть обсуждения касается конструкции PC и средств разработки программного обеспечения, которые используются для ге- нерации команд. Как уже говорилось в предисловии, эта книга — не попытка научить программированию. Поэтому здесь обсуждаются только те аспекты язы- ков программирования, которые мы действительно используем. Не рассматри- вается полностью и программирование на языках Си и ассемблера. Некоторые из вопросов сложны и поэтому им дана соответствующая трактовка. За допол- нительной информацией по программированию вы можете обратиться к другим книгам, список которых представлен в разделе «Литература». Информация, представленная в этой главе, является предпосылкой для по- нимания следующих глав. Если вы не найдете ответа на какой-то вопрос в по- следующей главе, то, возможно, вы обнаружите его здесь. Опытные программи- сты (и все те, кто желает углубиться в вопрос создания изображений), возмож- но, захотят бегло просмотреть эту главу при первом чтении и вернуться к ней позже. Обзор аппаратных средств PC Обзор ЦПУ Центральное процессорное устройство (ЦПУ) — сердце любой вычислительной системы. Все вычисления совершаются внутри ЦПУ. Большинство устройств, составляющих PC, предназначены для того чтобы поддерживать работу ЦПУ. В этой книге термины ЦПУ, процессор и микропроцессор будут использоваться как синонимы для описания этого основного элемента вычислительной машины. В IBM PC и IBM-совместимых PC используется семейство процессоров Intel 80 х 86. Это семейство объединяет процессоры, отличающиеся по производитель- ности на несколько порядков, — от 8088, который использовался в оригинальном PC, до «персональной суперЭВМ» 80486. Все представители этого семейства име- ют общее наследство — процессор 8086. Каждый новый процессор совместим с предыдущим поколением процессоров. Это означает, что код, написанный для процессора 8088, будет выполняться без модификации на процессоре 80486, но только быстрее. Однако код, написанный с учетом всех возможностей нового процессора, не будет выполняться на более старом. Совместимость по коду — это одновременно и благословение, и проклятие. Совместимость хороша тем, что старое программное обеспечение будет работать, когда PC заменяется на новый, более быстрый. Недостатком такой совместимо- сти является то, что все прикладные программы написаны главным образом для процессора 8088 и поэтому не используют преимущества более нового поколения процессоров. Возьмем, например, подготовку этой книги. Текст готовился с ис- пользованием старой версии WordStar, разработанной для процессора 8088, ко- торый применялся в оригинальном PC. Эта старая версия WordStar работала на процессоре 80386 (PS/2, модель 70). То, что программа выполняется без проблем на новом процессоре, является положительным моментом. Но, с другой стороны, хотя она и работает быстрее, ее возможности не расширяются пропорционально производительности нового процессора. Например, размер буфера для текста не изменяется при работе на новом процессоре.
Обзор аппаратных средств PC 11 Использование операционной системы, работающей в «реальном режиме», PC-DOS (два разных термина PC-DOS и MS-DOC будут в этой книге использо- ваться как синонимы) является еще одной причиной, почему на новом процессо- ре не доступны новые возможности. Эта операционная система разрабатывалась для процессоров 8088/86, установленных на оригинальном PC. Когда работает MS-DOS, она предполагает, что базовым является процессор типа 8088 и поэто- му не инициализирует новый процессор на работу в режиме собственной, более эффективной системы команд. Существует множество различных версий РС- DOS/MS-DOS, начиная с оригинальной версии 1.0, до новейшей 4.1. В различных версиях этой очень распространенной операционной системы было сделано мно- го усовершенствований. Однако ни одна из этих версий не работает в режиме, отличающемся от «реального». Более новые операционные системы, такие, как UNIX, AIX и OS/2, имеют преимущество в том, что используют предусмотренную для более современных процессоров собственную систему команд. Эти операционные системы могут пре- доставить больше возможностей и функций прикладным программам, написан- ным специально для работы в них. Они обеспечивают: а) Поддержку аппаратными средствами многозадачного режима. б) Защиту памяти при локализации ошибочной задачи и отладке. в) Расширенное адресное пространство для программных команд и данных. Эти операционные системы предоставляют большие возможности с одним ограничением — они не могут работать на старых процессорах 8088/8086. Для работы этих систем необходим, как минимум, процессор Intel 80286. В ближай- шее время появится новая версия OS/2 (возможно, с названием OS/2 386 или OS/3), которая будет работать только на процессорах 80386/486. Она будет ис- пользовать те возможности процессора 80386, которых нет даже у процессора 80286. Из-за широкого распространения и применения операционной системы MS- DOS все представленные в книге программы написаны для этой системы. Это не только вынуждает процессор работать в режиме 8088/86 (в «реальном режи- ме»), но также накладывает некоторые ограничения на имеющиеся ресурсы для наших программ создания изображений: а) Суммарный объем памяти, который может быть адресован в рамках при- кладной программы, составляет 640К байт. Это является существенным недостатком, так как отдельные изображения занимают объем 300К байт. б) Не доступен многозадачный режим работы. Существуют способы устранения этих ограничений, однако, они в некоторой степени специфичны для прикладной программы и мы не будем обсуждать и ис- пользовать их в этой книге. Чтобы расширить возможный круг читателей, для программ создания изображений будем использовать только операционную си- стему MS-DOS. Было бы бесполезно давать другие версии программ для других операционных систем до тех пор, пока эти системы не станут более распростра- ненными. Процессор 8088/86, выбор которого обусловлен использованием MS-DOS, можно охарактеризовать следующим образом:
12 Глава 1. Общие сведения а) ЦПУ способно адресовать 1М байт памяти в сегментах по 64К. б) Для управления архитектурой памяти с сегментной организацией исполь- зуются четыре специальных регистра сегментов: DS-регистр, или регистр сегментов для данных, CS-регистр, или регистр сегментов для кода, SS- регистр, или регистр сегментов стека, и, наконец, ES-регистр, или допол- нительный регистр сегментов (рис. 1.1). в) Сегменты начинаются на границах параграфа. Параграф имеет длину 16 байт. Границей параграфа является любой адрес, который делится на 16 без остатка. г) Системой обозначения, которая используется для определения участка памяти служит пара <сегмент:смещение>. Сегмент указывает на начало области памяти, а смещение содержит разность между начальным адре- сом сегмента и текущим адресом. Так как сегменты могут перекрываться, каждая область памяти может определяться более чем одной парой <сег- мент:смещение>. Другими словами, каждая пара <сегмент:смещение> определяет единственный в своем роде участок памяти; в свою очередь, другая пара <сегмент:смещение> также может определять тот же уча- сток. Так как длина смещения составляет 16 бит, максимальная длина любого сегмента будет 64К. д) Все четыре регистра сегментов могут отмечать одну и ту же область па- мяти. Примером этого является крошечная модель памяти в Turbo С, которая будет вкратце рассмотрена ниже. В действительности архитектура сегментной организации памяти процессо- ров Intel (при работе в «реальном режиме») далеко не идеально соответствует обработке графических изображений. Некоторые изображения, полученные с по- мощью цифрового видеопреобразователя, который подробно рассматривается в этой книге, занимают больше памяти, чем может содержаться в одном сегменте данных. Для получения данных, выходящих за границы одного сегмента, требу- ется дополнительная команда. Эта дополнительная команда — то «наказание» за использование процессоров Intel, которое проявляется в снижении произво- дительности. Процессоры серии Motorola 68000 с большим линейным адресным пространством более пригодны для обработки изображений. Тем не менее мы бу- дем использовать для своих целей только процессоры Intel, так как они наиболее широко распространены. Замечание по быстродействию процессора. Обработка изображений — это процесс, включающий огромное количество операций с числами. Большие объ- емы обработки могут потребовать подключения для решения этой задачи не- скольких PC. Хотя все программы, разработанные для этой книги, будут выпол- няться на оригинальном PC с процессором 8088 (4.77 мГц), результаты можно получить намного быстрее на процессоре 80386 (20 мГц). Графические адаптеры для PC Введение Графический адаптер — встроенная в PC электронная схема, которая позволя- ет PC выводить на экран, кроме текста, графические изображения. Графиче-
Обзор аппаратных средств PC 13 Номера битов 15-------------------------О 7 —.....—О 7-----------------О Регистры Данных АХ АН AL ВХ ВН BL СХ СН CL DX DH DL Регистры индексов и указателей SP Stack Pointer ВР Base Pointer SI Source Index DI Destination Index Указатель инструкции и Флаги Номер бита С — перенос, Р — четность, А — внешний перенос, Z — нуль, S — знак, Т — флаг внутреннего прерывания, I — возможность прерывания, D — направление и О — пе- реполнение. Все другие биты зарезервированы фирмой Intel для использования в бу- дущем.
14 Глава 1. Общие сведения ское изображение состоит из ряда точек и линий, которые могут размещаться в рабочей области экрана подключенного к PC монитора. Графика освобождает программиста от ограничений текстового режима работы. Когда используется термин графический адаптер, это подразумевает наличие как текстового, так и графического режима работы. В промышленности термин адаптер дисплея подразумевает часто только текстовые режимы. В этой книге оба термина ис- пользуются для обозначения одного и того же: способности выводить на экран как текст, так и графические изображения. Возможности графических дисплеев компьютеров IBM PC и совместимых с ними постоянно возрастают. Первый графический адаптер — Цветной графиче- ский адаптер (CGA) — мог выводить изображение на экран 320 х 200 элементов в четырех цветах или 640 х 200 в двух цветах. Сопоставьте это с современной технологией дисплеев на основе Видео графической матрицы (VGA), которая может поддерживать разрешение экрана 640 х 480 в 16 цветах или 320 х 200 в 256 цветах. Успехи в технологии графических дисплеев связаны со следующим: а) более низкими ценами (особенно на микросхемы памяти видео ОЗУ); б) спросом на графические пользовательские интерфейсы; в) применением для автоматизации издательского процесса и для создания изображений. Графические адаптеры с высоким разрешением и высокой производительно- стью — постоянная цель разработчиков. Широко разрекламированный фирмой IBM адаптер VGA уже отходит на второй план. На горизонте появляются ап- паратные средства для дисплеев приемлемой стоимости с разрешением в мил- лионы элементов. Персональные компьютеры и рабочие станции середины 90-х годов будут, вероятно, снабжаться аппаратурой, дающей разрешение 1024 х 1024 элементов. Такое разрешение вместе с 8-12-бит плоскостями цветовой памяти позволит получать на экране дисплея изображения типа фотографий. Корпора- ция Apple уже разработала свое программное обеспечение QuickDraw 32, которое обеспечивает близкие к этому возможности. В отличие от Apple фирма IBM никогда не достигала ошеломляющих резуль- татов в разработке графических возможностей. Основное внимание она уделя- ла бизнес-программам и программам по презентации своей графики. Адаптер VGA — это попытка IBM удержаться на плаву в постоянной гонке разработок графических аппаратных средств. Под опекой IBM адаптеру VGA предназна- чается роль графического стандарта, как это было в случае адаптера CGA и Расширенного графического адаптера (EGA). В мире компьютеров IBM и совместимых с ними существует не менее восьми стандартов адаптеров дисплея: 1. MDA (Монохромный адаптер дисплея). 2. HGA (Графический адаптер Hercules). 3. CGA (Цветной адаптер дисплея). 4. Адаптер дисплея в IBM PC Jr. 5. EGA (Расширенный графический адаптер).
Обзор аппаратных средств PC 15 6. MCGA (Многоцветовая графическая матрица), который имеется на моде- лях 25 и 30 компьютеров IBM PS/2. 7. VGA (Видео графическая матрица), который имеется на моделях 50, 55, 60, 70 и 80 компьютеров PS/2. 8. Карта адаптера 8514/А и монитор IBM 8514. Интересно отметить, что все стандарты дисплеев в представленном выше списке, за исключением Графического адаптера Hercules, разрабатывались и внедрялись корпорацией IBM. В настоящее время в продаже есть множество доступных графических адаптеров других фирм; некоторые из них имеют луч- шие характеристики, чем даже у адаптера 8514/А. Они, однако, не представлены в списке, потому что им еще придется заработать статус «стандарта». Многие адаптеры дисплея, выпускаемые независимыми производителями, просто эму- лируют один или несколько стандартов, представленных выше. Режимы работы и разрешение Получение изображений высокого качества требует определенного, минимально приемлемого уровня функциональности графического адаптера. Конечно, гра- фический адаптер, выводящий на экран одновременно четыре цвета (такой, как CGA), может дать приемлемое качество только черно-белых или двухцветных изображений. Цветное изображение, полученное с помощью адаптера CGA, в лучшем случае можно оценить как неважное. Возникает вопрос, какова мини- мальная функциональность графического адаптера, необходимая для приемле- мого проявления изображений? Для того чтобы можно было дать это опреде- ление, в таблице 1.1 подробно рассматриваются возможности большинства ада- птеров дисплея. Текстовые режимы, имеющиеся на различных адаптерах, не приведены в таблице, так как они не так важны для программ, работающих с изображениями, как графические режимы. Все изображения в этой книге попадают в одну из двух категорий: полуто- новые и цветные изображения. Полутоновые изображения выводятся на экран с разрешением 320 х 200 либо в 16, либо в 64 уровнях яркости, с разрешением 640 х 200 в 16 уровнях яркости и с разрешением 640 х 480 также в 16 уровнях яр- кости. Цветные изображения могут выводиться только с разрешением 320 х 200 с использованием 256 цветов. Для проявления изображений с многоуровневой шкалой яркости или цвет- ных изображений требуются настраиваемые палитры и цветовые регистры. Что- бы обеспечить правильный цветовой баланс для вывода изображений на экран, RGB-компоненты цветовых регистров должны быть установлены напрямую. Хо- тя адаптер EGA дает одновременно 16 цветов из палитры в 64 цвета, отсутствие настраиваемых цветовых регистров делает его мало пригодным для приложений, связанных с созданием изображений. Для увеличения числа кажущихся цветов, которые может выводить адаптер EGA, за счет потери в разрешающей способ- ности дисплея можно использовать метод псевдотонирования (см. гл. 7). Так как целью процесса обработки графической информации является получение изображений типа фотографий, то потеря в разрешении на создание псевдопо- лутонового изображения, по-видимому, не достаточно эффективный путь.
16 Глава 1. Общие сведения Таблица 1.1. Возможности графических адаптеров дисплея Разрешение Цвета CGA PCJr EGA MCGA VGA 320 X 200 4 ♦ ♦ ♦ ♦ ♦ 640 X 200 2 ♦ ♦ ♦ ♦ ♦ 160 х 200 16 ♦ 320 х 200 16 * ♦ ♦ 320 х 200 256 ♦ ♦ 640 х 200 4 ♦ 640 х 200 16 ♦ ♦ 640 х 350 2 ♦ * 640 х 350 4 ♦ 640 х 350 16 ♦ * 640 х 480 2 * ♦ 640 х 480 16 * Настраиваемая палитра * ♦ ♦ ♦ Настраиваемые цветовые регистры * * Примечания 1. Адаптер HGA не представлен, так как он не выводит цвета. Адаптер MDA не представлен, так как он не поддерживает графику. Карта адаптера 8514/А отсутствует, так как она доступ- на лишь немногим из-за очень большой стоимости. 2. Настраиваемая палитра позволяет составить определенный набор одновременно выводимых на экран цветов из большего числа возможных цветов. 3. Цветовые регистры являются настраиваемыми, если их индивидуальные RGB (red — крас- ный, green — зеленый и blue — синий)-компоненты могут изменяться программой. Ответ на вопрос: «Какова минимальная функциональность графического ада- птера, необходимая для приемлемого проявления изображений?» теперь должен быть нам очевиден. Адаптер VGA имеет все возможности, необходимые для той работы по созданию изображений, которая проделана в данной книге. (Примеча- ние: Адаптер VGA имеет те минимальные возможности, которые требуются для экспериментирования в области создания изображений. Любой адаптер с мень- шими, чем у VGA, возможностями не даст удовлетворительных результатов. Тем не менее было бы желательно иметь большие возможности, чем у VGA.) По этой причине все программы создания изображений, представленные в книге, пред- полагают наличие адаптера VGA. Никакой поддержки для других адаптеров дисплея не предусмотрено. VGA-адаптер — стандартное оборудование на 50-х и более старших моделях компьютера PS/2. Кроме того, он поставляется фирмой IBM и другими производителями как графическая карта расширения для более старых PC. Вложение средств в VGA адаптер и монитор является предпосыл- кой для серьезной работы по созданию изображений на PC и РС-совместимых компьютерах. В этой книге используются только те режимы и разрешения адаптера VGA, которые предусмотрены системой BIOS (Основная система ввода-вывода). Лю- бой доступ к VGA, за исключением режима с 256 цветами и разрешением 320 х 200, осуществляется через графическую библиотеку в системе Turbo С. Так как разработчики Turbo С решили не поддерживать этот графический режим,
Обзор аппаратных средств PC 17 он будет поддерживаться через функции языков ассемблера и Си. Аппаратные средства VGA фирмы IBM способны работать во многих нестандартных режи- мах, включая 256 цветов при разрешении вплоть до 360 х 480. В статьях Ричарда Уилтона (см. «Ссылки и дальнейшее чтение») имеется информация о том, как достичь этих нестандартных режимов. Использование графических возможно- стей системы Turbo С будет обсуждаться чуть ниже. Палитры Палитра определяется в словаре как тонкая доска с отверстием для большого пальца на одном конце, на которой художники держат и смешивают краски. Она также определяется как набор цветов на такой доске. В терминах компьютерной графики палитра — это набор цветов, который можно одновременно выводить на экран цветного монитора. Для вывода цвета на экран используют различные механизмы. Три таких механизма показаны на рис. 1.2-1.4. Рассмотрим каждый из них отдельно. На рисунке 1.2 представлена конфигурация палитры для адаптера EGA. Этот адаптер обсуждается здесь, чтобы возможности VGA можно было сравнить с ним. В случае EGA 4 бит данных, которые хранятся в видеопамяти, образуют индекс в структуре данных палитры. Четыре бита видеоданных дают 16 воз- можных значений индекса цвета в диапазоне от 0 до 15. Все 16 элементов в структуре палитры являются 6-бит номерами цвета. Цвет, обозначенный номе- ром цвета, фиксирован; он не может изменяться. Тем не менее палитры. Четыре бита видеоданных дают 16 возможных значений индекса цвета в диапазоне от 0 до 15. Все 16 элементов в структуре палитры являются 6-бит номерами цве- та. Цвет, обозначенный номером цвета, фиксирован; он не может изменяться. Тем не менее каждый элемент в палитре может изменяться — до любого из 64 возможных номеров цвета. Это позволяет одновременно выводить на экран 16 цветов из общей палитры в 64 цвета. Отметим, что монитор для адаптера EGA является цифровым (как и монитор для адаптера CGA). Шесть бит цифровой информации о цвете, r’g’b’RGB, выводятся непосредственно из адаптера на экви- валентный вход монитора. Шесть бит цвета дают в итоге 26 или 64 комбинации, т. е. 64 цвета. При инициализации EGA палитра заполняется цветами, которые совместимы с цветами для CGA. Это сделано для того, чтобы гарантировать обратную со- вместимость с большой базой программного обеспечения, написанного для CGA. В табл. 1.2 приведены значения по умолчанию, которые хранятся в структуре данных палитры EGA. Чтобы вывести на экран монитора EGA элемент изображения светло-мали- нового цвета, нужно было бы поместить десятичное число 13 в соответствующий адрес видеопамяти. В действительности же вызывается графическая функция Turbo С «putpixel», которая передает 13 как запрашиваемый цвет. По мере того как вносятся изменения в содержание палитры, все элементы изображения, соответствующие изменяемому индексу, будут принимать новый цвет. При выводе изображения на экран можно вызывать специальные причуд- ливые эффекты, изменяя номера цвета, которые хранятся в палитре. Некоторые из этих эффектов действительно впечатляют. 2-3
18 Глава 1. Общие сведения 6 <------А------ 6 цифровых сигналов формата r’g’b’RGB дают 26=64 возможных цвета Цифровой RGB-монитор Рис» 1.2» Конфигурация палитры EGA. В графических адаптерах VGA возможны два разных метода управления па- литрой. Метод зависит от того, какой графический режим используется. Все графические режимы — за исключением режима 13Н (13 hex), с разрешением 320 х 200 и 256 цветами — используют по существу тот же механизм, что и адаптер EGA. EGA-совместимое устройство палитры VGA показано на рис. 1.3. Оно отличается тем, что элементы палитры содержат указатели на один из 256 регистров вместо 63 номеров цвета, которые используются в EGA. Еще большее различие между этими двумя графическими адаптерами состоит в том, что ин- дивидуальные (красная, зеленая и синяя) RGB-компоненты цветовых регистров могут настраиваться в VGA-адаптере и являются фиксированными в EGA. При условии, что каждая цветовая компонента (красная, зеленая и синяя) исполь- зуют 6 бит информации, мы будем иметь 218 различных возможных цветов. Не
Обзор аппаратяых средств PC 19 Приведены все режимы,за исключением 13Н с 256 цветов Красный Зеленый Синий возможных цветов Рис. 1.3. Конфигурация палитры VGA. Примечание ЦАП — цифроаналоговый преобразователь. трудно сосчитать, что это составит 262144 различных вариантов цвета, 16 из которых можно одновременно вывести на экран. Для вывода такого множества цветов должен использоваться аналоговый RGB-монитор. Как показано на рисунке, для преобразования 18 бит цифровой информации о цвете в аналоговые сигналы, которые требуются для управле- ния монитором, необходимы цифроаналоговые преобразователи. Это делает VGA (как сам графический адаптер, так и монитор) более дорогим, чем EGA, одна- ко, более высокие цветовые возможности компенсируют разницу в цене. Режим VGA 13Н — это специальный цветовой режим. В этом режиме могут выводиться на экран изображения с низким разрешением (320 х 200) в 256 различных цве- 2*
20 Глава 1, Общие сведения Рис. 1.4. Режим 13Н конфигурации палитры VGA. тах. Рис. 1.4 иллюстрирует устройство палитры, которое используется в этом специальном режиме. В действительности, было бы точнее сказать, в этом спе- циальном режиме палитра отсутствует. Память дисплея в режиме 13Н сегменти- рована в восемь различных битовых плоскостей в отличие от четырех, которые используются во всех других режимах. Наличие 8-бит плоскостей означает, что элемент видеоданных может принимать значения от 0 до 255. В этом режиме устройство палитры не используется, и значение видеоданных непосредствен- но индексирует цветовой регистр. Значение видеоэлемента 10 принимает цвет, определенный RGB-значениями цветового регистра 10 и т. д. При использовании
Обзор аппаратных средств PC 21 Таблица 1.2. Цвета EGA по умолчанию Индекс палитры Цвет Элемент палитры 0 Черный 0 1 Синий 1 2 Зеленый 2 3 Циановый 3 4 Красный 4 5 Малиновый 5 6 Коричневый 20 7 Светло-серый 7 8 Темно-серый 56 9 Голубой 57 10 Светло-зеленый 58 11 Светло-циановый 59 12 Алый 60 13 Светло-малиновый 61 14 Желтый 62 15 Белый 63 этого графического режима изображение выводится на экран в 256 различных цветах из возможных 262144. Каждый из этих 256 цветов может быть любым из набора в 262144. При инициализации режима 13Н цветовые регистры загружаются следую- щим образом. (Эта информация взята из Справочного руководства по интерфей- сам аппаратных средств IBM PS/2.) а) Первые 16 цветовых регистров загружаются значениями, которые соот- ветствуют цветам CGA и EGA, приведенным в табл. 1.2. б) Вторые 16 цветовых регистров загружаются равномерно расположенными оттенками серого цвета. в) Последние 216 цветовых регистров содержат значения, основанные на мо- дели оттенков, насыщения и интенсивности, настроенной так, чтобы обес- печить удобный характерный цветовой набор, который охватывает широ- кий диапазон значений цвета. Генерация палитры Чтобы точно вывести на экран преобразованное в цифровой код изображение, необходимо сгенерировать и установить палитру, которая отражает цветовое со- держание изображения. Для полутоновых изображений необходима палитра со шкалой яркости, а для цветных изображений нужна палитра, которая наибо- лее близко соответствует цветам в оригинальном изображении. Существуют два метода для подгонки пары палитра/изображение. В первом методе, который ис- пользуется для полутоновых изображений, изображение выводится на экран в равнораспределенных оттенках серого цвета. Другими словами, в адаптере VGA сначала программируется палитра со шкалой яркости и изображение появля-
22 Глава 1. Общие сведения ется на экране в этой палитре. Палитра выбирается как некоторый диапазон в шкале яркости, и она не связана непосредственно с содержанием изображения. Каждый элемент данных изображения заносится в полутоновую палитру под своим числовым значением. Во втором методе подгонки пары палитра/изображение используется алго- ритм для построения оптимальной цветовой палитры, наиболее точно отража- ющей цвета в оригинальном изображении. В этом случае палитра, которая ис- пользуется для вывода на экран переведенного в цифровой код изображения, генерируется из реальных цветов в этом изображении. Это способ принципиаль- но отличается от способа, где изображение выводится в наиболее подходящих цветах из предварительно заданной палитры. Выделение информации о палитре из изображения приводит к значительно более реалистичному представлению изображения на экране. Алгоритм, который используется для выбора палитры для цветного изображения, будет обсуждаться в гл. 5. При любом методе подгонки необходима регулировка цветовых регистров в адаптере дисплея. Это одна из тех областей, в которых адаптер VGA с его регули- руемыми цветовыми регистрами заметно превосходит EGA. При фиксированных цветах, как в адаптере EGA, нельзя точно вывести на экран ни полутоновые, ни цветные изображения. Во всех случаях (для EGA) палитра, которая использу- ется для вывода изображения на экран, будет наиболее подходящей подгонкой из имеющихся фиксированных цветов. Это приводит к искажению цвета в вы- водимом на экран изображении. В противоположность этому, VGA позволяет устанавливать цветовые регистры на цвета, которые точно выводят на экран преобразованное в цифровой код изображение. Палитры с равномерной шкалой яркости легко генерируются. Их генерация основана на том факте, что серый цвет состоит из одинаковых частей красно- го, зеленого и синего. Если не принимать во внимание оттенки серого цвета, то результатом равенства всех RGB-компонентов цветового регистра будет се- рый, белый или черный цвет. Значения, которые используются в цветовых реги- страх, зависят от числа требуемых уровней яркости и фактора гамма-коррекции (это обсуждается чуть ниже) для используемого монитора. Можно сгенериро- вать две различные полутоновые палитры, что весьма полезно для обработки изображений, выполненной в этой книге: 16-уровневую и 64-уровневую шкалу яркости. Очевидно, что 16-уровневая шкала требует использования 16 цвето- вых регистров, в то время как для 64-уровневой нужно 64 цветовых регистра. 16-уровневую шкалу яркости можно использовать в любом из 16-цветовых гра- фических режимов VGA, тогда как 64-уровневую шкалу только в специальном видеографическом режиме 13Н, который обсуждался выше. В табл. 1.3 приве- дены значения, которые загружаются в цветовые регистры для 16 и 64 уровней полутоновой палитры. Генерация полутоновой палитры, которая будет восприниматься как равно- распределенная, усложняется двумя обстоятельствами: нелинейностью монито- ров для компьютера и нелинейностью системы зрительного восприятия. Что- бы компенсировать нелинейность монитора, вводится фактор гамма-коррекции. Гамма-коррекция это попытка установить линейную связь между числовым зна- чением данных в цветовом регистре и результирующим свечением (яркостью) дисплея. Если бы мониторы для компьютера были полностью линейны, значения
Обзор аппаратных средств PC 23 полутоновой палитры были бы также линейны. Нелинейность гамма-функции видна из табл. 1.3. Формула коррекции: Исправленное значение = ехррп (значение)/степенной гамма-фактор] Значение степенного гамма-фактора для большинства мониторов находится в диапазоне от 1,8 до 2,2. Для генерации палитры, приведенной в табл. 1.3, исполь- зовалось значение 2,2. Значения 16-уровневой шкалы яркости брались непосред- ственно из кода IBM BIOS. Дополнительную информацию по гамма-коррекции вы можете найти в «Руководстве по растровой графике» корпорации CONRAC. В разделе по графическим функциям системы Turbo С будет приведен пример установления палитры для адаптера VGA. Порт принтера Порт принтера является аппаратным интерфейсом для связи PC с цифровым видеопреобразователем (см. гл. 3). Рассмотрим только ту часть функций прин- терного порта, которые требуются для преобразователя. Обсуждение других воз- можностей, предоставляемых принтерным портом, вы можете найти в техниче- ском описании для вашего типа PC. Связь между цифровым видеопреобразователем и PC через принтерный порт была выбрана в основном из-за высокой пропускной способности данных, хотя существуют и другие причины для такого выбора. Благодаря параллельному типу связи, принтерный порт может передавать данные быстрее, чем последова- тельный порт. При параллельной связи могут посылаться одновременно многие биты данных. Для цифрового видеопреобразователя была бы возможна и после- довательная связь, но в этом случае время, необходимое для пересылки данных изображения в компьютер, было бы слишком велико. Кроме того, для последо- вательной связи цифрового видеопреобразователя с компьютером потребовалась бы дополнительная электрическая цепь. Поэтому с учетом пропускной способ- ности и стоимости был выбран параллельно соединенный цифровой преобразо- ватель. Другим преимуществом подсоединения цифрового преобразователя через па- раллельный принтерный порт является его совершенство и стабильность. На протяжении всей эволюции PC он по существу не изменялся. Это означает, что цифровой преобразователь может быть подсоединен к любому из компьютеров PC, от оригинального IBM PC до новых PS/2, включая совместимые. Это от- крывает двери к более широкому использованию цифрового преобразователя. Следует отметить, что параллельный порт для компьютеров серии PS/2 имеет более высокие возможности, оставаясь при этом обратно совместимым. Одна- ко цифровой преобразователь не использует ни одной из новых возможностей, обеспеченных параллельным портом в PS/2. Наконец, использование принтерного порта для связи между PC и цифровым преобразователем освобождает от необходимости вставлять в компьютер плату интерфейса. Это означает, что остается свободным разъем для платы и, кро- ме того, не нужно разрабатывать специальную карту интерфейса для каждого типа шины PC. Если бы использовалась внутренняя карта интерфейса, потре- бовалось бы вплоть до трех ее вариантов: один для оригинального PC, другой для шины ISA/EISA (расширенная АТ-шина) и третий для шины MCA, которая
24 Глава 1. Общие сведения Таблица 1.3. Значения полутоновой палитры Цветовой регистр 16 уровней 64 уровня Красный Зеленый Синий Красный Зеленый Скиний 0 00 00 00 00 00 00 1 05 05 05 00 00 00 2 08 08 08 00 00 00 3 11 11 11 00 00 00 4 14 14 14 00 00 00 5 17 17 17 01 01 01 6 20 20 20 01 01 01 7 24 24 24 02 02 02 8 28 28 28 03 03 03 9 32 32 32 03 03 03 10 36 36 36 04 04 04 11 40 40 40 05 05 05 12 45 45 45 06 06 06 13 50 50 50 07 07 07 14 56 56 56 08 08 08 15 63 63 63 09 09 09 16 — — — 10 10 10 17 — — — 10 10 10 18 — — — 11 11 11 19 — — — 12 12 12 20 — — — 13 13 13 21 — — — 14 14 14 22 — — — 15 15 15 23 — — — 16 16 16 24 — — — 18 18 18 25 — — — 19 19 19 26 — — — 20 20 20 27 — — — 21 21 21 28 — — — 22 22 22 29 — — — 23 23 23 30 — — — 24 24 24 31 — — — 25 25 25 32 — — — 26 26 26 33 — — — 27 27 27 34 — — — 28 28 28 35 — — — 29 29 29 36 — — — 31 31 31 37 — — — 32 32 32 38 — — — 33 33 33 39 — — — 34 34 34 40 — — — 35 35 35 41 — — — 36 36 36 42 — — — 37 37 37 43 — — — 39 39 39 44 — — — 40 40 40 45 — — — 41 41 41
Обзор аппаратных средств PC 25 Таблица 1.3. (Продолжение) Цветовой регистр 16 уровней 64 уровня Красный Зеленый Синий Красный Зеленый Ситгий 46 — — — 42 42 42 47 — — — 43 43 43 48 — — — 44 44 44 49 — — — 46 46 46 50 — — — 47 47 47 51 — — — 48 48 48 52 — — — 49 49 49 53 — — — 50 50 50 54 — — — 52 52 52 55 — — — 53 53 53 56 — — — 54 54 54 57 — — — 55 55 55 58 — — — 56 56 56 59 — — — 58 58 58 60 — — — 59 59 59 61 — — — 60 60 60 62 — — — 61 61 61 63 — — — 63 63 63 используется в последних моделях семейства PS/2. Как вы уже можете оценить, связь с цифровым преобразователем через параллельный порт все существенно упрощает. Недостаток использования параллельного порта на PC для связи с цифро- вым преобразователем состоит в том, что, когда будет использоваться цифровой преобразователь, принтер нужно отсоединять. Перемена кабелей может быть достаточно утомительной. Существуют два решения этой проблемы. Во-первых, можно было бы добавить к цифровому преобразователю дополнительное аппа- ратное средство, чтобы сделать его прозрачным для подсоединенного принтера. Это повысило бы стоимость и сложность цифрового преобразователя, что в дан- ном случае не приемлемо. Во-вторых, можно использовать блок переключения на принтер. Селекторные блоки для принтера доступны и относительно не доро- ги. При одном положении переключателя выбирался бы принтер, при другом ци- фровой преобразователь. Это — наилучшее решение. (При использовании блока переключения A/В обязательно используйте очень короткие кабели. Цифровой преобразователь не проектировался на присоединение к PC через кабель длиной более 12—15 фут. Скорость передачи данных между цифровым преобразователем и PC требует использования коротких кабелей.) Параллельный порт состоит из 12 выходных и 5 входных линий, которые могут управляться и проверяться программами для PC. Все линии совместимы со стандартом ТТЛ. Эти линии ввода-вывода можно разбить на три различные категории, характеристики которых различаются несущественно. Первая категория включает 8-бит выходной порт, который обычно исполь-
26 Глава 1. Общие сведения зуется для передачи символьных данных между PC и присоединенным принте- ром. Эти выходные линии способны пропускать выходной ток 2,6 мА и входной ток 24 мА. При подключении к электрической схеме цифрового преобразова- теля выходной порт служит для установки счетчика элементов изображения. Эти линии используют положительную логику. То есть когда программа уста- навливает любой из этих битов на высокий уровень (или на единицу), соответ- ственно возрастает напряжение на выходном контакте (приблизительно до 5В). Для одновременной установки всех восьми битов используется 8-бит команда OUT. Эти сигналы записываются в документации IBM, начиная с + Data Bit О по + Data Bit 7. Выходные сигналы имеются в одном выходном адресе, которо- му в низкоуровневом коде цифрового преобразователя дано символическое имя PrtPortBase. Между выводами выходного сигнала (выводами принтерного порта) и данными, записанными в выходной порт, существует однозначное соответствие: Бит данных 76543210 Номер вывода 98765432 Вторая категория сигналов ввода-вывода описывает остальные четыре выход- ных линии, которые имеются в параллельном интерфейсе принтерного порта. Эти линии выводятся с помощью устройства, повышающего выходное напря- жение до 5В, через резистор 4,7 кОм. Линии способны проводить только 7 мА и используют как положительную, так и отрицательную логику, как показано ниже: Имя выходного сигнала принтерного порта Тип логики — Strobe отрицательная — Auto Feed отрицательная — Select Input отрицательная — Initialize Printer положительная Отрицательная логика подразумевает, что, когда линия устанавливается на высокий уровень, действительный выходной контакт будет на нижнем уровне, т. е. выходной сигнал инвертирован по отношению к данным. Эти вспомогательные выходные сигналы имеются в одном выходном адресе, которому дано символическое имя PrtPortCont. Они организованы следующим образом: Бит данных 7 6 5 4 X X X X 3 2 10 I-------Strobe ----------------------Auto Feed ----------------------Initialize Printer ----------------------Select Input ‘Xй указывает на биты, которые мы не учитываем.
Обзор аппаратных средств PC 27 Последнюю группу сигналов ввода-вывода составляют пять входных линий. Эти линии представляют единую нагрузку ТТЛ Low Signal на присоединенный к ним источник сигналов ТТЛ. Когда выполняется команда In из адреса PrtPortln, уровни сигналов на входных контактах запираются и становятся доступными для программы. Из-за того что на пути сигнала между соединением принтерно- го порта и шиной данных процессора имеются аппаратные средства, считывание данных не соответствует уровням сигналов на входных контактах соединитель- ного устройства. Эти входные линии, или сигналы, используют как положитель- ную, так и отрицательную логику, как показано ниже: Имя входного сигнала принтерного порта Тип логики + Busy — Acknowledge + Р. End + Select — Error отрицательная положительная положительная положительная положительная Если входной сигнал использует отрицательную логику, внешний сигнал бу- дет считываться как сигнал высокого уровня (или логическая единица). По- ложительная логика подразумевает, что если внешний сигнал — сигнал вы- сокого уровня, то он будет считываться как сигнал с высоким уровнем (т. е. без инверсии). Эти входные сигналы организуются внутри PrtPortln следующим образом: Бит данных 7 6 5 4 3 2 1 0 I X X X •---------Error I—-— + Select ---------------------------- + P.End ----------------------------Acknowledge ----------------------------+ Busy “X” указывает на биты, которые мы не учитываем. Отрицательная логика, которую использует сигнал +Busy, требует специаль- ного учета при разработке цифрового преобразователя (см. гл. 3). Для IBM PC и совместимых с ним компьютеров определены три стандарт- ных адреса ввода-вывода параллельного порта. Они приводятся в технических справочных руководствах как Parallel 1, 2 и 3. Эти три различных адреса позво- ляют бесконфликтно подключить к одному PC вплоть до трех принтеров (или цифровых преобразователей). Стандартное адресное распределение для этих па- раллельных портов представлено ниже:
28 Глава 1. Общие сведения Имя, приписанное порту ввода-вывода Parallel 1 Parallel 2 Parallel 3 PrtPortBase 3BC hex 378 hex 278 hex PrtPortln 3BD hex 379 hex 279 hex PrtPortCont 3BE hex 37A hex 27A hex Parallel 1. используется в интерфейсной карте «Монохромный адаптер дис- плея IBM/Принтер» и как стандартный параллельный принтерный порт в се- мействе компьютеров PS/2. Parallel 2 используется картой принтерного адаптера IBM. Parallel 3 не имеет определенного стандартного назначения. Как уже обсуждалось выше, для связи между PC и цифровым преобра- зователем используются только три адреса ввода-вывода. Другие аппаратные средства могут использовать этот интерфейс, если способны функционировать с ограниченным числом имеющихся входных и выходных линий. Чтобы цифровой преобразователь мог работать с таким малым количеством контрольных линий, для его аппаратных средств разработаны специальные методы мультиплексиро- вания. Эти методы наряду с другими вопросами взаимодействия подробно обсу- ждаются в гл. 3. Средства разработки программного обеспечения Ниже рассмотрены относящиеся примеры разработки программного обеспече- ния, а также средства и методы использования языков ассемблера и Си, так как оба этих языка применяются для цифрового преобразователя, описанного ниже в данной книге. Везде, где только возможно, использовался язык Си, так как он удобен и более производителен. Код на ассемблере применялся в тех случаях, когда от программы требовалась максимальная скорость работы. Далее в кни- ге обсуждаются только нестандартные проблемы программирования на обоих упомянутых языках. Использование ассемблера Хотя была предпринята попытка сохранить минимальное число команд на языке ассемблера, тем не менее довольно большое их количество оказалось необходи- мым. Там, где лимитирующей стадией являлось время выполнения команд, код записывался на языке ассемблера. Тщательно продуманный язык ассемблера все же быстрее, чем код, сгенерированный компилятором Си, несмотря на опти- мизирующие свойства компилятора. Там, где счет идет на команды процессора, следует использовать ассемблер. В программах на языке ассемблера, представленных в этой книге, можно ис- пользовать любой транслятор, который генерирует Microsoft-совместимый объ- ектный код. Как Macro Assembler/2 фирмы IBM, так и MASM фирмы Microsoft использовались на различных этапах разработки цифрового видеопреобразова- теля без каких-либо затруднений. Можно было бы применять и TASM фирмы Borland. Исходный код на языке ассемблера транслируется для получения объ- ектного файла, который в конечном счете компонуется с объектным файлом
Средства, разработки программного обеспечения 29 (файлами), созданным компилятором языка Си. Если обнаружатся ошибки при компоновке объектного кода, сгенерированного ассемблером, с объектным кодом, сгенерированным компилятором, вам, возможно, придется изменить опции ком- поновщика системы Turbo С, связанные с различением символьных имен верхне- го и нижнего регистров. Это, вероятно, решит любые проблемы несовместимости при компоновке. Любой код ассемблера, который будет скомпонован с Turbo С, должен соот- ветствовать определенному формату. Этот формат включает специальные согла- шения по именам, которые будут использоваться для всех названий функций, а также для сегментов кода (Text) и данных (DATA и BSS). Имена сегментов дан- ных зависят от модели памяти, с которой в конечном счете будет скомпонован код ассемблера. Строгое соблюдение предусмотренного формата будет гарантией того, что компоновщик Turbo С сможет разрешить все внешние ссылки на код ассемблера. Для того чтобы научиться писать код ассемблера, необязательно по- нимать все детали формата, который используется для этого кода. Формат всех файлов ассемблера по существу одинаков и может быть скопирован из программ, представленных в этой книге. Пример необходимого формата кода языка ассем- блер для малой модели памяти приведен в листинге 1.1. Подробное описание форматов для всех моделей памяти вы можете найти в «Руководстве пользова- теля Turbo С», а также в других книгах, посвященных программированию на языке Си. Последним необходимым условием для успешного объединения кодов ассем- блера и Си является понимание того, как данные могут распределяться между ними. Хотя то, как распределяются данные, зависит от модели памяти, которая используется компилятором. Распределение данных и передача параметров — это два важных вопроса, которые обсуждаются в следующем разделе. Код языка ассемблера в листинге 1.1 является частью поддержки, предусмо- тренной для режима VGA 13Н (графического режима с разрешением 320 х 200 и одновременным выводом 256 цветов). Такая поддержка предусмотрена здесь потому, что версия 2.0 Turbo С автоматически не дает необходимой поддержки. Этот код необходимо было включить, так как режим VGA 13Н используется во всей книге. Как оказывается, обеспечение поддержки режима VGA 13Н дела- ет код программ обслуживания цифрового преобразователя легко переносимым между различными типами Си-компиляторов. Действие этого кода обсуждается в разделе, посвященном библиотеке функций поддержки VGA. Использование Turbo С В книге при написании всех программ использовалась интегрированная среда компилятора Turbo С. Характеристики этой среды — скорость, гибкость, друже- ственный интерфейс, отладчик, система поддержки project-файлов — позволяют быстро создавать прикладные программы, а также находить и устранять ошиб- ки. Система project-файлов, хотя и не является полной системой типа UNIX- make, вполне достаточна, чтобы постоянно содержать объектные модули, кото- рые составляют прикладную программу для обработки изображений, в органи- зованном и обновленном состоянии без особых усилий со стороны пользовате- ля. Полное описание возможностей системы project-файлов вы можете найти в
30 Глава 1. Общие сведения «Руководстве пользователя Turbo С». Для обсуждения ниже приведен образец project-файла для программы ввода цветного изображения из гл. 5. Он называ- ется cvideo.prj: graphics, lib digitize.obj vgagraph.obj egavga.obj рсх vga cvideo (misc.h1 pcx.h) (misc.h vga.h pcx.h) (misc.h vga.h pcx.h digitize.h) Листинг 1.1. Поддержка видеорежима 13Н с помощью языка ассемблера ; Режим адаптера VGA 13Н----функции поддержки режима с разрешение» ; 320x200 и одновременным выводом на экран 256 цветов ; разработчик Крэйг А. Линдли ; Последние изменения внесены 11/29/89 .TEXT segment byte public ’CODE’ DGROUP group _DATA,_BSS assume cs:.TEXT,ds:DGROUP,ss:DGROUP .TEXT ends .DATA segment word public ’DATA’ .DATA ends _BSS segment word public ’BSS’ .BSS ends .TEXT segment byte public ’CODE’ ; Программы для режима с 256 цветами и разрешением 320x200 » ; Процедура _PutPixel256 ; Эта процедура используется для непосредственного доступа к ; видеопамяти, когда адаптеры VGA или MCGA находятся в режиме 13Н. » ; CALL: вызывается из Си. ; PROTOTYPE: void PutPixel256 (unsigned Col, unsigned Row, unsigned ; Color); ; INPUT: Все параметры, передаваемые этой функции, находятся в стеке. ; Стек должен содержать следующее: Color по адресу [Ьр+8], ; Row по адресу [Ьр+6] и Col по адресу [Ър+4] • 1 1Файл misc.h полностью приведен в конце книги на стр. 506.
Средства, разработки программного обеспечения 31 OUTPUT: на экране VGA кодифицируется определенный элемент изображения. Использует и очищает регистры АХ, ВХ, СХ, DX Public _PutPixel256 _PutPixel256 proc near push bp mov bp,sp mov ex, [bp+8] ;получает цвет элемента изображения ;в цветовом регистре mov ах, [Ьр+6] ;получает номер Rob mov bx, [Ьр+4] ; получает номер Col вычисляет адрес элемента изображения в видеобуфере mov dx,320 ; каждый элемент изображения занимает один байт mul dx ; находит смещение add bx,ax ;ВХ - х + 320 ♦ у mov ax,0A000H ; сегмент видеобуфера mov es,ax ;es:BX указывает на элемент ;изображения в буфере mov es:[bx],cl ; обновляет элемент изображения pop bp ret _PutPixel256 endp ; Процедура . .GetPixel256 Эта процедура используется для непосредственного доступа к видеопамяти, когда VGA или MCGA находятся в режиме 13Н. CALL : вызывается из Си PROTOTYPE: unsigned PutPixel256 (unsigned Col, unsigned Rob); INPUT: все параметры этой функции находятся в стеке. Стек должен содержать следующее: Rob по адресу [Ьр+6] и Col по адресу [Ьр+4] . OUTPUT: определенный элемент изображения на экране VGA возвращается в АХ. Использует и очищает регистры АХ, ВХ, СХ, DX Public _GetPixel256 _GetPixel256 proc near push bp mov bp,sp mov ax,[bp+6] ; получает номер Rob mov bx,[bp+4] ; получает номер Col
32 Глава 1. Общие сведения Листинг 1.1. (Продолжение) ; вычисляет адрес элемента изображения в видеобуфере mov dx,320 ;каждый элемент изображения занимает ;один байт mul dx ; находит смещение add bx,ax ;ВХ » х + 320 ♦ у mov ax,0A000H ;сегмент видеобуфера mov es,ax ;es:BX указывает на элемент ;изображения в буфере mov al, es: [bx] ;получает значение элемента изображения xor ah, ah ; маскирует наиболее значащий байт pop bp ret _GetPixel256 endp -TEXT ends end Имя выполняемого файла, созданного project-файлом, это имя project-файла без расширения .prj. Другими словами, данный выполняемый файл, который генерируется project-файлом, будет называться cvideo.exe. Project-файл понимает по встроенным правилам, что любой файл с именем, записанным в project-файле без расширения, это Си-файл. Он изначально зна- ет также, что должен скомпилировать Си-файл до того, как этот файл может быть скомпонован с другими файлами. Строка в только что показанном project- файле, содержащая cvideo, иллюстрирует эти правила. Далее эта строка говорит, что файл cvideo зависит от всех include-файлов: pcx.h, vga.h, digitize.h и misc.h. Другими словами, если временная метка (дата и время, когда файл последний раз изменялся) для любого из include-файлов является более новой чем для cvideo.с (что указывает на изменения в include-файлах, которые могут повлиять на работу cvideo.с), то cvideo.c должен быть перекомпилирован до того, как он может быть скомпонован. Все остальные файлы, приведенные в project-файле, представляют собой библиотечные файлы (расширение .lib) и объектные фай- лы (расширение .obj). Чтобы образовать полную программу, эти файлы долж- ны быть скомпонованы с файлом cvideo.obj, который создается компилятором. Project-файл будет автоматически следить за всем процессом. Графические возможности и функции системы Turbo С Основная причина, по которой система Turbo С выбрана как платформа для создания программ обработки изображений, состоит в том, что она имеет доста- точно полную и простую для использовании библиотеку графических функций. Вторая, также очень важная причина, заключается в том, что в этой системе используется язык Си в стандарте ANSI с прототипами функций (возможно, это наиболее важная особенность языка Си в стандарте ANSI). Графическая библиотека выполняет множество важных функций, таких, как автоматическое опознавание видеографического адаптера (определение типа графического ада- птера, который установлен в компьютере), инициализация нужного драйвера
Средства разработки программного обеспечения 33 для графических аппаратных средств и обеспечение богатого набора функций для обработки текста и графики на графическом устройстве вывода. Если бы эти возможности не предоставлялись системой Turbo С, разработка графиче- ских программ и программ по созданию изображений занимала бы значительно больше времени. Как уже говорилось выше, для того чтобы использовать графические воз- можности Turbo С, необходимо иметь для компоновки определенные файлы. Обязательно нужен файл graphicsJib, чтобы его можно было скомпоновать с объектным кодом, созданным компилятором для данной прикладной программы обработки изображений. Этот файл содержит программу, которая выполняет все графические функции. Когда программа работает, должны быть доступны гра- фический драйвер для видеографического адаптера VGA и все файлы шрифтов (с расширением .chr). Так как VGA является единственным адаптером, который используется в книге, при работе программы необходимо иметь только файл драйвера egavga.bgi (способ устранения этого требования будет также предста- влен). Файлы шрифтов не используются, так как они не имеют значения для представленных здесь программ (однако они могут понадобиться для ваших соб- ственных прикладных программ). Библиотека функций адаптера VGA Различные программы обработки изображений, представленные в книге, требу- ют дополнительных графических возможностей кроме тех, которые поддержи- ваются системой Turbo С. Все эти дополнительные функции, собранные вместе, образуют библиотеку функций адаптера VGA. Библиотека состоит из 10 функ- ций (вызываемых из прикладных программ, написанных на языке Си), которые обеспечивают особые графические режимы. Некоторые из этих функций поддер- живают режим VGA 13Н, который доступен для графических адаптеров VGA и MCGA и не поддерживается системой Turbo С. Другие возвращают текущий видеорежим или загружают полутоновые палитры. Все эти функции показаны в листингах 1.1 и 1.2 и описаны в табл. 1.4. Функции, представленные в библиотеке функций адаптера VGA, применя- ются в большинстве программ, которые даны в книге. Чтобы использовать эти функции, прикладная программа должна включить в свой код файл vga.h и за- тем скомпоноваться с файлом vga.obj, созданным при компилировании библио- теки функций адаптера VGA. Для всех программ в книге драйвер дисплея для графических адаптеров EGA/VGA связывается с самой программой. Это означает, что он включается в код этой программы во время компоновки, а не считывается с диска в процессе выполнения программы. Таким образом, все представленные здесь программы являются полностью самостоятельными, что очень полезно. Как драйверы дис- плея, так и шрифты, можно включить в прикладную программу одинаковым образом. Чтобы сделать это, код для файла драйвера или шрифта — например, egavga.bgi — должен быть преобразован в объектный файл с помощью сервисной программы bgiobj.exe, имеющейся в пакете Turbo С. В результате преобразова- ния мы получим файл egavga.obj, который можно скомпоновать непосредственно с прикладной программой. И наконец, прикладной программе необходимо сооб- щить о присутствии скомпонованных файлов шрифта и драйвера дисплея, вы- звав функции registerbgidriver и registerbgifont. Символическое имя шрифта или 3-3
34 Глава 1. Общие сведения драйвера дисплея следует передать этим функциям в виде параметров. В слу- чае драйвера дисплея EGA/VGA этим символическим именем будет EGA/VGA driver. Когда заканчивается исполнение программы bgiobj.exe, она преобразует символическое имя. Не забудьте опустить подчеркивание в начале символиче- ского имени при передаче его в виде параметра функциям registerbgi. В ваших графических прикладных программах к функции registerbgi следует обращаться до вызова initgraph. Эти приемы показаны и обсуждаются в библиотеке функций адаптера VGA в той части, где говорится о функции InitGraphics. Листинг 1.2. Библиотека функций адаптера VGA Ниже приводится содержание файла vga.h: /фффффффффффффффффффффффффффффффффффффффф/ /♦ Header-файл ♦/ /♦ для функций доступа к VGA ♦/ /* разработан в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли */ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 11/28/89 */ /фффффффффффффффффффффффффффффффффффффффф/ /* определяет режимы VGA с разрешением 320x200» 640x200 и 640x480 */ «define LRMAXCOLS 320 /* режим с 256 цветами и разрежением 320x200 */ «define LRMAXROWS 200 «define LRVIDEOMODE 0*13 /* как полученный при вызове GetVideoMode ♦/ «define MRMAXCOLS 640 /* режим с 16 цветами и разрежением 640x200 */ «define MRMAXROWS 200 «define MRVIDEOMODE 0*0Е /* как полученный при вызове GetVideoMode */ «define HRMAXCOLS 640 /* режим с 16 цветами и разрежением 640x480 */ «define HRMAXROWS 480 «define HRVIDEOMODE 0*12 /* как полученный при вызове GetVideoMode */ «define SCREENWIDTHINCHES (double) 9.500 «define SCREENHEIGHTINCHES (double) 7.125 /♦ Следующие значения используются для преобразования вертикальных и горизонтальных элементов в дюймы экрана. Используются в вычислениях аспект- ного отношения при вращении изображений. Могли бы быть вычислены для всех разрешений, но в настоящий момент используется только низкое разрешение. ♦/
Средства, разработки программного обеспечения 35 /♦ LRPIXELSPERINCHHORIZ равно LRMAXCOLS/SCREENWIDTHINCHES. Определяется как постоянная скорости в операциях с плавающей точкой. LRINCHESPERPIXELHORIZ равно 1/LRPIXELPERINCHHORIZ. ♦/ «define LRPIXELSPERINCHHORIZ (double) 33.68421053 «define LRINCHESPERPIXELHORIZ (double) 0.02968750 /♦ LRPIXELSPERINCHVERT равно LRMAXROWS/SCREENHEIGHTINCHES. Определяется как постоянная скорости в операциях с плавающей точкой. LRINCHESPERPIXELVERT равно 1/LRPIXELPERINCHVERT. ♦/ «define LRPIXELSPERINCHVERT (double) 28.07017544 «define LRINCHESPERPIXELVERT (double) 0.03562500 «define MAXCOLREGVAL 63 /♦ Определения функций VGA ♦/ /* Прототипы функций языка ассемблера */ void PutPixel256 (unsigned Col, unsigned Row, unsigned Color); unsigned GetPixel256 (unsigned Col, unsigned Row); /* Прототипы функций Си */ void InitGraphics (void); unsigned GetVideoMode (void); void Set256ColorMode (void); void SetAColorReg (unsigned RegNum, unsigned Red, unsigned Green, unsigned Blue); void GetColorReg (unsigned RegNuia, unsigned *Red, unsigned *Green, unsigned *Blue); void LoadGrayl6Palette (void); void LoadGray64Palette (void); Ниже следует содержание файла vga.c: у****************************************/ /♦ Функции графического адаптера VGA ♦/ /♦ разработаны в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли */ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 12/04/89 */ «include <stdio.h> «include <process.h> «include <dos.h> «include <graphics.h> «include "misc.h" 3*
36 Глава, 1. Общие сведения Листинг 1.2. (Продолжение) #include "pcx.h" tinclude "vga.h" /* равнораспределенная 16-уровневая шкала яркости */ ColorRegister Gray16ColorPalette[MAXPALETTECOLORS] » { 0, 0, 0, 5, 5, 5, 8, 8, 8,11,11,11, 14,14,14,17,17,17,20,20,20,24,24,24, 28,28,28,32,32,32,36,36,36,40,40,40, 45,45,45,50,50,50,56,56,56,63,63,63 }; /* равнораспределенная 64-уровневая шкала яркости */ ColorRegister Gray64ColorPalette[МАХ256РALETTECOLORS] = { 0, 0, 0, 0, 0, 0, 0, О, О, О, О, О, О, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,10,10,10,10,10,11,11,11,12,12,12, 13,13,13,14,14,14,15,15,15,16,16,16, 18,18,18,19,19,19,20,20,20,21,21,21, 22,22,22,23,23,23,24,24,24,25,25,25, 26,26,26,27,27,27,28,28,28,29,29,29, 31,31,31,32,32,32,33,33,33,34,34,34, 35,35,35,36,36,36,37,37,37,39,39,39, 40,40,40,41,41,41,42,42,42,43,43,43, 44,44,44,46,46,46,47,47,47,48,48,48, 49,49,49,50,50,50,52,52,52,53,53,53, 54,54,54,55,55,55,56,56,56,58,58,58, 59,59,59,60,60,60,61,61,61,63,63,63 }; /♦ Начало функций VGA ♦/ /* Инициализация графической подсистехы ♦/ void InitGraphics( void ) int g.driver, g.mode, g.error; /♦ Проверяет, открыта ли графическая система. В любом случае закрывает ее, а затем опять открывает. Иначе память для драйвера будет размещаться каждый раз, когда вызывается эта функция. ♦/ closegraph(); /♦ инициализация графических переменных ♦/ g_driver = g_mode = g_error = 0;
Средства разработки программного обеспечения 37 /♦ При вызове registerbgidriver, который показан ниже, драйвер дисплея компонуется с прикладной программой. Предполагается, что драйвер был преобразован из egavga.bgi в egavga.obj с помощью преобразующей программы bgiobj и скомпонован с прикладной программой. Строка egavga.obj должна находиться в файле .prj для прикладной программы. ♦/ registerbgidriver(EGAVGA_driver); init graph (ftg_dri ver, ftg.mode ,"*'); g_error = graphresult(); if (g_error < 0) { printf("Initgraph error: %s.\n”, grapherrormsg(g_error)); exit(EGraphics); restorecrtmodeO; /♦ Эта функция получает текущий видеорежим из активного видеоконтроллера. ♦/ unsigned GetVideoMode( void ) { union REGS regs; regs.h.ah = OxOF; /♦ спрашивает текущий видеорежим ♦/ int86(VIDEO,ftregs,regs); return(regs.h.al); /♦ Установка адаптера VGA в режим с 256 цветами и разрешением 320x200.*/ void Set256ColorMode( void ) { union REGS regs; setgraphmode (VGALO) ; /* переходит в графический режим */ regs.h.ah ж 0; /♦ теперь в режим 13Н для 256 цветов ♦/ regs.h.al e 0x13; int86(VIDEO,ftregs,ftregs); /* Установка индивидуального цветового регистра адаптера VGA ♦/ void SetAColorReg(unsigned RegNum, unsigned Red, unsigned Green, unsigned Blue) { union REGS regs;
38 Глава 1. Общие сведения Листинг 1.2. (Продолжение) /* Если установлен графический режим, мы можем загружать цветовой регистр в ЦДЛ. ♦/ /♦ устанавливает цветовой регистр ♦/ regs.h.ah я 0x10; regs.h.al я 0x10; regs.h.bx я RegNum; regs.h.dh я Red; regs.h.ch = Green; regs.h.cl я Blue; int86(VIDEO,feregs,Aregs); /* Получение цветовых компонентов цветового регистра адаптера VGA */ void GetColorReg(unsigned RegNum, unsigned ♦Red, unsigned ♦Green, unsigned ♦Blue) union REGS regs; /♦ Если установлен графический режим, мы можем считывать цветовой регистр из ЦАП. ♦/ /♦ получает компоненты цветового регистра ♦/ regs.h.ah я 0x10; regs.h.al я 0x15; regs.x.bx я RegNum; int86(VIDEO,&regs,&regs); /♦ записывает полученные значения по указателям ♦/ ♦ Red « regs.h.dh; ♦ Green s regs.h.ch; ♦ Blue « regs.h.cl; /♦ загружает полутоновую палитру ♦/ void LoadGrayl6Palette(void) struct palettetype pelette; unsigned Index; union REGS regs; /♦ Если установлен графический режим, мы можем произвести загрузку нашей палитры и цветовых регистров в ЦАП. В последовательном порядке устанавливается палитра, и цветовые регистры приобретают значения шкалы яркости. ♦/
Средства разработки программного обеспечения 39 palette.size «16; for (Index » 0; Index < MAXPALETTECOLORS; Index++) palette.colors[Index] « Index; /* устанавливает блок цветовых регистров ♦/ regs.h.ah » 0x10; regs.h.al ж 0x12; regs.x.bx » 0; regs.x.ex » MAXPALETTECOLORS; _ES » FP_SEG(Grayl6ColorPalette); regs.x.dx »FP_0FF(Grayl6ColorPalette); int86(VIDEO,ftregs ,&regs); /♦ настраивает заново созданную палитру ♦/ setallpelette(Apalette); /♦ загружает полутоновую палитру ♦/ void LoadGray64Palette(void) { union REGS regs /♦ Эта 64-уровневая палитра может быть загружена» если только адаптер VGA находится в режиме 13Н для 256 цветов. Механизм палитры в этом режиме не используется. Цветовые регистры непосредственно загружаются в ЦАП. ♦/ /♦ устанавливает блок цветовых регистров */ regs.h.ah « 0x10; regs.h.al = 0x12; regs.x.bx = 0; regs.x.ex = 64; _ES » FP_SEG(Gray64ColorPalette); regs.x.dx =FP_0FF(Gray64ColorPalette); int86(VIDEO,ftregs,&regs); Работа с границами сегментов, указателями данных и моделями памяти Без сомнения, изображения, переведенные в цифровой код, занимают значитель- ный объем памяти при хранении и во время обработки. Изображения, получен- ные с помощью цифрового преобразователя, представленного в книге, могут за- нимать до 300К байт памяти (1К байт = 1024 бит), тогда как программа, которая используется для обработки переведенных в цифровой код изображений, обычно занимает не более 64К. Иначе говоря, до 64К кода используется для обработки
40 Глава 1. Общие сведения до 300К данных изображения. Зная предельный объем кода и данных, необхо- димый для наших прикладных программ, можно выбрать модель памяти для разработки этих программ. Модель памяти определяет, к какому объему кода и данных будет иметь доступ прикладная программа, написанная на языке Си, и какие типы указателей будут приниматься по умолчанию. В Turbo С существует шесть разных моделей памяти: крошечная, малая, сред- няя, компактная, большая и гигантская. Каждая из них имеет различные пре- делы, накладываемые на размер сегментов кода и данных, которые составля- ют программу. Точные определения моделей памяти вы найдете в «Руководстве пользователя Turbo С». Для всех программ в этой книге (за исключением про- граммы view.exe в гл. 6) оказалась пригодной малая модель памяти. В описании Turbo С для малой модели памяти говорится следующее: Сегменты кода и данных не перекрываются, так что вы будете иметь 64К кода и 64К статических данных. Дополнительный сегмент и сегмент стека на- чинаются с того же адреса, что и сегмент данных. Всегда используются ука- затели типа Near. Это является хорошим размером для средней прикладной программы. Таблица 1.4. Библиотека функций адаптера VGA Библиотека функций адаптера VGA составлена из функций, написанных как на языке ассемблера, так и на языке Си. Функции ассемблера содержатся в файле vgagraph.asm, а функции Си в файле vga.c. 1. Размещение элемента изображения на дисплее VGA в ре- жиме 13Н с разрешением 320x200. Прототип void PutPixel256 (unsigned Col, unsigned Row, unsigned Color); Где «Со1» (столбец) и «Row» описывают ячейку, в которую нужно по- местить элемент, a «Color» указывает номер цветового регистра, кото- рый должен использоваться для элемента. Собственный диапазон значений «Со1» — от 0 до 319, значений «Row» — от 0 до 199 и значений «Color» — от 0 до 255. Проверка ошибок этой функцией не выполняется. Действие Эта функция, написанная на языке ассемблера, помещает элемент изобра- жения на дисплей в режиме VGA 13Н с разрешением 320 х 200 с помощью прямого доступа к видеопамяти VGA. Организацией видеопамяти для ре- жима VGA 13Н является линейный массив из 64000 байт, расположенных в сегменте А0000 hex памяти компьютера PC. Чтобы напрямую поместить элемент изображения, необходимо вычислить, в каком из 64000 байт ви- деопамяти находится этот элемент, и записать в него значение цвета. Вы- числение адреса производится следующим образом: Адрес памяти VGA = х-координата + 320 * ^-координата Фактически адрес определяется как смещение положения элемента изо- бражения в видеосегменте. Если сегмент и смещение элемента известны, то несложно записать в него значение цвета. Перед тем как эту функ- цию использовать, необходимо, чтобы адаптер VGA находился в режиме
Средства разработки программного обеспечения 41 13Н. Пример того, как действует программа для получения изображений в 256 цветах, рассмотрен в разделе, посвященном вводу цветных изобра- жений. Эта функция необходима для представленных в книге программ обработки изображений, потому что Turbo С не поддерживает режим 13Н. 2. Считывание элемента изображения с дисплея VGA в ре- жиме 13Н с разрешением 320x200. Прототип unsigned GetPixel256 (unsigned Col, unsigned Row); Описание параметров приведено выше. Действие Действие этой функции аналогично действию обсуждавшейся выше функ- ции PutPixel256, за исключением того, что значение элемента изображения считывается, а не записывается. Положение определенного элемента изо- бражения в памяти VGA вычисляется тем же способом, что и выше. 3. Инициализация графической подсистемы. Прототип void InitGraphics (void); Параметры не используются. Действие Данная функция инициализирует графическую систему PC. По этой при- чине во всех представленных здесь программах ввода и обработки изобра- жений используется вызов InitGraphics. Из листинга видно, эта программа достаточно проста. Первая операция программы — закрытие графической библиотеки. Это может быть сделано независимо от того, открывалась ли ранее библиотека или нет. Такое действие необходимо, чтобы предотвра- тить последовательные вызовы InitGraphics (умышленные или случайные) и не допустить, таким образом, размещения множественных копий памяти, которые требуются для функций драйвера дисплея. Закрытие библиотеки перед ее открытием будет гарантией того, что произойдет только одно такое размещение. Всякий раз необходимо только одно размещение памяти. Далее, драйвер дисплея для EGA/VGA регистрируется графической си- стемой. После этого графическая система будет знать, что код драйвера, в данном случае EGAVGA.OBJ, уже был ранее скомпонован с кодом при- кладной программы. Регистрация драйвера препятствует его загрузке с диска во время выполнения InitGraphics. Тот же метод используется и в случае шрифтов. Регистрация приводит к объединению кода драйвера дисплея (или кода шрифта) с графической программой. (При регистрации драйвера или шрифта размер прикладной программы возрастает на размер кода драйвера или шрифта.) Функция initgraph выполняет всю инициализацию графической системы. Она принимает в качестве параметров адреса переменных графического драйвера (g\_driver) и графического режима (g\_mode), а также путь в каталог на диске, где должен находиться файл графического драйвера.
42 Глава 1. Общие сведения Таблица 1.4. (Продолжение) При успешном выполнении функции initgraph, будут инициализированы g\_driver и g\_node. В программе, показанной выше, путь к графическому драйверу записывается как NULL , потому что драйвер не загружается с диска. Это очень удобно, так как в этом случае прикладная программа будет действовать самостоятельно и ей не придется во время исполнения обращаться к диску, чтобы найти драйвер дисплея. Если при выполнении initgraph обнаруживается ошибка, передается сооб- щение об этой ошибке и выполнение программы прерывается. После вызова последней функции restorecrtmode в данной программе, гра- фический адаптер переходит в текстовой режим. Большинство программ по обработке изображений запускаются в текстовом режиме для взаимодей- ствия с оператором, а затем после ввода оператором исходных данных пе- реключаются в графический режим. При вызове restorecrtmode программа запускается в текстовом режиме. Функция setgraphmode может быть вы- звана позднее, чтобы очистить экран и перевести систему в графический режим. Используйте setgraphmode и restorecrtmode, чтобы переключаться из текстового режима в графический и обратно. 4. Получение текущего видеорежима. Прототип unsigned Get VideoMode (void); Параметры не используются. Действие Get VideoMode возвращает номер текущего видеорежима. В настоящее вре- мя для PC-совместимых компьютеров существует 20 видеорежимов: от 0 до 13 hex, хотя не на каждом компьютере доступны сразу все режимы. В данной книге наиболее важными являются следующие три режима: режим 13 hex (13П) с одновременным выводом 256 цветов и разрешением 320 х 200, режим 0Е hex (16 цветов, 640 х 280) и режим 12 hex (16 цветов, 640 х 480). Для использования в программах по обработке изображений эти видео- режимы имеют символические имена LRVIDEOMODE, MRVIDEOMODE и HRVIDEOMODE соответственно. Символические имена определяются в файле vga.h, который показан в верхней части листинга 1.2. Действие этой функции, как и других функций библиотеки VGA, заключа- ется в передаче соответствующих значений в регистры процессора и выпол- нении системных вызовов BIOS. Используются функции ЮН прерывания системы BIOS. 5. Выбор видеорежима 13Н (256 цветов, разрешение 320x200). Прототип void Set256ColorMode (void) Переменные не используются. Действие Эта функция переводит графический адаптер VGA (или MCGA) в режим 13Н с разрешением 320 х 200 и одновременным выводом на экран 256 цве- тов. Как уже говорилось выше, эта функция необходима потому, что си- стема Turbo С не поддерживает данный графический режим. В частности,
Средства, разработки программного обеспечения 43 в адаптер VGA при вызове этой функции загружается принимаемая по умолчанию палитра из 256 цветов. В коде данной функции требуется вызов функции setgraphmode, чтобы установить определенные внутренние переменные Turbo С, которые по- казывают, что система находится в графическом режиме. Однако режим VGALO сохраняется не долго, потому что при вызове BIOS сразу устана- вливается режим 13Н. Вызывается функция установки видеорежима (код функции 0) и с этого момента адаптер дисплея находится в специальном видеорежиме (256 цветов). Этот режим сохраняется до тех пор, пока не вызывается функция restorecrtmode или не закрывается графическая би- блиотека. 6. Установка цветового регистра VGA (или MCGA). Прототип void SetAColorReg (unsigned RegNum, unsigned Red, unsigned Green, unsigned Blue); где RegNum — число, показывающее с каким из 256 цветовых регистров нужно работать. Red, Green, и Blue — цветовые компоненты, которые должны быть переданы этому регистру. Каждая компонента может иметь значение в диапазоне от 0 до 63. RGB-компоненты 0, 0, и 0 дают черный цвет, а компоненты 63, 63, 63 — белый. Если все три цветовые компоненты имеют одинаковое значение, мы получим серый цвет. 7. Получение компонентов цветового регистра VGA (или MCGA). Прототип void GetColorReg (unsigned RegNum, unsigned *Red, unsigned * Green, unsig- ned *Blue); где RegNum — число, показывающее с какого из 256 цветовых регистров нужно считывать данные. Red, Green, и Blue — указатели ячейки, в кото- рой должны храниться определенные компоненты цветовых регистров. Действие Эта функция выполняет действие, обратное тому, которое выполняет функ- ция, приведенная выше. Она возвращает RGB-компоненты определенного цветового регистра. 8. Загрузка 16-уровневой полутоновой палитры. Прототип void LoadGrayl6Palette (void); Параметры не определяются. Действие Эта функция загружает в адаптер VGA 16-уровневую полутоновую пали- тру, которая обсуждалась выше и приведена в табл. 1.3. Она также иници- ализирует и устанавливает структуру палитры, используемую для видео- режима с одновременным выводом 16 цветов. Палитра инициализируется в прямом отображении, т. е. элемент палитры 0 указывает на цветовой регистр 0, элемент палитры 15 указывает на цветовой регистр 15 и т. д.
44 Глава 1. Общие сведения Таблица 1.4. (Продолжение) Вспомните, что для видеорежима с 16 цветами значение элемента изобра- жения в памяти дисплея является индексом в структуре палитры, содер- жащим номер цветового регистра, который будет использоваться для выво- да элемента изображения на экран. Поэтому для обеспечения такого соот- ветствия структура палитры должна инициализироваться при вызове этой функции. Для загрузки сразу всех 16 цветовых регистров (от 0 до 15) ис- пользуется система BIOS. Чтобы выполнить загрузку, в СХ-регистр про- цессора передается число 16, показывающее, сколько цветовых регистров нужно загрузить; в регистр дополнительного сегмента (\_ES) загружается сегмент, содержащий массив Grayl6ColorPalette; в регистр DX передает- ся смещение в сегменте массива; и, наконец, выполняется видеопрерыва- ние системы BIOS. После загрузки цветовых регистров с помощью системы BIOS используется функция Turbo С setallpalette для настройки инициали- зированной палитры. При вызове setallpalette палитра, которая в данный момент используется адаптером VGA для вывода изображений на экран, заменяется новой, сгенерированной этой программой. Все цвета на экране монитора сразу же будут преобразованы в оттенки серого цвета: каждый оттенок будет зависеть от значения видеоданных. Если значением элемента изображения был 0, этот элемент станет на экране черным. Если значением было число 15, элемент примет белый цвет. В случае промежуточных зна- чений элемент изображения будет выводится на экран в одном из оттенков серого цвета. В общем случае, чем больше числовое значение видеоданных, тем выше уровень яркости. (Адаптер VGA должен находиться в необхо- димом графическом режиме до того, как эта функция будет выполняться. В противном случае, если графический режим вводится позднее, значения цветовых регистров, которые мы установили, будут заменены при инициа- лизации нового графического режима.) 9. Загрузка 64-уровневой полутоновой палитры. Прототип void. LoadGray64Palette (void); Параметры не определяются. Действие Данная функция аналогична LoadGrayl6Palette, за исключением того, что в этом случае загружается 64-уровневая полутоновая палитра. Перед тем, как функция будет выполняться, адаптер VGA должен находиться в режи- ме 13Н. Видеорежим 13Н является единственным режимом, позволяющим выводить на экран дисплея одновременно более 16 цветов. Он необходим нам для получения изображений в 64 уровнях яркости. При этом не все воз- можности режима 13Н, который в принципе позволяет выводить на экран 256 цветов, будут использоваться. Как вы уже знаете, в этом видеорежиме видеоданные являются прямым индексом цветового регистра (механизм па- литры не используется). Например, чтобы элемент изображения выводился на экран в нулевом уровне по шкале яркости, он должен иметь значение 0. Чтобы получить на экране элемент в 37-м уровне по шкале яркости, зна- чение элемента должно составлять 37. Это единственное, чем отличается действие данной функции от действия функции, обсуждавшейся выше.
Средства, разработки программного обеспечения 45 Малую модель памяти можно представить наглядно следующим образом: Сегментация в малой модели памяти Регистры сегмента CS—► TEXT class ‘CODE’ code DS---► ES SS DGROUP: DATA class ‘DATA* инициализированные данные BSS class ‘DATA’ неинициализированные данные Локальная память Размер сегмента До 64 Кбайт До 64 Кбайт Указатель стека SP Стек Глобальная память До предела памяти Значение различных разделов памяти, показанных на диаграмме выше, будет постоянно обсуждаться в этой книге. Общий максимальный размер прикладных программ, использующих малую модель памяти, составляет 128К байт. Это свя- зано с тем, что размер сегментов кода как и сегментов данных может составлять 64К и программа может иметь по одному сегменту каждого типа. Следует обра- тить внимание на то, что вы можете точно определить указатели и функции любого типа независимо от выбранной модели памяти. Как же мы сможем обрабатывать изображения размером 300К, если область памяти, которая отводится для данных, составляет 64К? Ответ: данные изобра- жения находятся не в сегменте данных прикладной программы, а размещаются в той области памяти, которая называется в системе Turbo С «глобальной динами- ческой областью памяти». Другими словами, сегмент данных, предназначенный для программы, содержит только специфические данные этой программы, а не действительные данные изображения. Когда распределяется глобальная дина- мическая область памяти, помните: а) могут быть распределены все имеющиеся ресурсы ОЗУ; б) могут размещаться блоки более 64К; в) для доступа к размещенным блокам необходимо использовать 32-разряд- ные указатели типа FAR. Глобальная динамическая область памяти включает всю память компьютера, начиная с границы прикладной программы и заканчивая предельным размером 640К, который предоставляется системой MS-DOS. Для того чтобы управлять глобальной динамической областью памяти, используются функции размеще- ния глобальной памяти системы Turbo С farmalloc, farcalloc, farcoreleft, farfree и
46 Глава 1. Общие сведения farrealloc. В малой модели памяти глобальная динамическая область отличает- ся от локальной динамической области памяти. Поэтому нужно позаботиться, чтобы прикладные программы не смешивали методы управления памятью при использовании малой модели памяти. Хотя программа может разместить для изображения большой кусок памяти, она сталкивается с проблемой обработки данных, которые выходят за границы сегмента размером 64К. Как вы помните, сегментированная архитектура процес- соров Intel (при работе в реальном режиме, на который рассчитана MS-DOS) обеспечивает легкий доступ к тем данным, которые полностью заключены в сег- менте размером 64К. Однако когда данные выходят за границы сегмента, для доступа к ним требуется дополнительная команда. Эта команда замедляет вы- полнение программ, которые обрабатывают большие объемы данных или имеют большой объем кода. Проблемы, связанные с ограниченным размером сегмента, влияют как на код на языке Си для прикладной программы по обработке изобра- жений, так и на низкоуровневый код на языке ассемблера для драйвера цифро- вого видеопреобразователя, который описывается в гл. 4. Система Turbo С имеет встроенный механизм для управления большими объемами данных, которые пе- ресекают границы сегмента. Низкоуровневая программа на языке ассемблера должна выходить из этой ситуации сама, используя дополнительные команды. Проблема сегментации для прикладной программы по обработке изображе- ний решается, если использовать для доступа к данным указатели типа HUGE. Указатели HUGE являются специальной разновидностью 32-разрядных указа- телей типа FAR. Указатели обоих типов состоят из 16-разрядного сегмента и 16-разрядного смещения. Указатели HUGE отличаются тем, что они всегда на- ходятся в своей «нормализованной форме», т. е. основная информация по адре- сам, которая содержится в указателе HUGE, хранится в его сегментной части. Так как сегменты всегда начинаются на границах 16-байт параграфов, смещение указателя HUGE всегда находится в диапазоне от 0 до 15, при этом сегментная часть содержит основную информацию по адресам. Для того чтобы нормализовать указатель, необходимо преобразовать его в полный 20-бит адрес и затем использовать правые 4 бит как значение смещения и левые 16 бит как значение сегмента. Например, чтобы преобразовать значе- ние ненормализованного указателя 3412:DF03 (стандартное обозначение пары сегмент:смещение, которая используется в процессорах Intel для определения ячейки памяти) в его нормализованный вид, необходимо: а) сдвинуть 16-ричное значение сегмента 3412 влево на 4 бит, получив при этом 34120; б) добавить к 34120 значение смещения DF03, получив в результате 42023 hex; в) последние 4 бит являются значением смещения, которое равно 3; г) 16 старших значащих бит являются значением сегмента 4202. Таким образом, нормализованным значением указателя 3412:DF03 будет 4202:0003. Процесс нормализации происходит в каждом случае после работы с указателем, при этом время выполнения программы увеличивается. Указатели типа HUGE в отличие от указателей FAR в результате процесса нормализации всегда указывают на единственный в своем роде адрес памяти.
Средства, разработки программного обеспечения 47 Так как сегменты могут перекрываться, различные указатели FAR могут отсы- лать к одному и тому же физическому адресу памяти. Например, все приведен- ные ниже указатели FAR указывают на одну и ту же ячейку памяти: 0000:015А 000А:00ВА 0014:001А Указатель HUGE, эквивалентный 0015:000А, будет единственным указателем типа HUGE, который указывает на эту ячейку. Это одно из преимуществ использования указателей HUGE. Уникальность каждого адреса памяти делает возможными арифметические операции с указа- телями. Сравнить два указателя FAR 0000:015А и 0014:001А, чтобы проверить их равноправность, не удастся, даже если оба этих указателя указывают на одну и ту же ячейку памяти. Если сравнивать указатели HUGE, то такое сравнение всегда будет проведено как полагается из-за уникальности каждого указателя. Другим преимуществом указателей HUGE является то, что они могут исполь- зоваться для доступа к структурам данных, которые выходят за границы сегмен- та. Описанный выше процесс нормализации приводит к тому, что границы сег- мента становятся прозрачными для программиста. Это делает указатели HUGE идеальными для прикладных программ обработки изображений. Чтобы пока- зать их достоинство, рассмотрим, что произойдет с указателем типа FAR, когда будет достигнута граница сегмента. Если, например, указатель имел значение 0010:FFFF и затем его значение выросло на 1, новым значением будет 0010:0000. Значение смещения изменится без приращения значения сегмента. Очевидно, что указатель не станет действовать там, где мы ожидали. Использование указателя HUGE гарантирует, что приращение значений сегмента и смещения происходит корректно. При этом пересекается граница сегмента и предоставляется возмож- ность доступа к данным другого сегмента. Чтобы разместить память для буфера данных изображения, указатель FAR, переданный функцией размещения глобальной динамической области памяти, перед использованием непосредственно приводится к типу HUGE. Ниже пред- ставлен фрагмент программы, который иллюстрирует этот метод: char huge *PictureData; printf("Allocating Picture Buffer\n"); /♦ размещает буфер для изображения и обнуляет его ♦/ if ((PictureData s (char huge ♦) farcalloc(RasterSize, (unsigned long) sizeof(char))) яж NULL) printf ("Digitize Error - Not enough memory\n"); exitO; > Когда программа заканчивает использование буфера для данных изображе- ния, память передается в систему следующим образом: farfree((char far ♦)PictureData);
48 Глава 1. Общие сведения Указатель типа HUGE PictureData, перед тем как будет использоваться функцией farfree, приводится к своему первоначальному виду. На уровне языка ассемблера работа с указателями не совсем проста для программиста. Можно использовать два метода, которые позволяют указате- лям корректно пересекать границы сегментов. В первом методе программа на языке ассемблера вынуждена держать значения указателей в нормализованном виде, как это делает программа на языке Си. Несмотря на то что такой метод во всех случаях работает корректно, он увеличивает число команд, что программа низкого уровня не может себе «позволить». Второй метод использует определен- ные программы проверки, помещенные в код для контроля за пересечением гра- ниц сегментов. При обнаружении пересечения границ сегмента, чтобы исправить значения сегмента и смещения соответствующим образом, используется допол- нительный код. В коде цифрового преобразователя прикладная программа высокого уровня на языке Си передает HUGE-указатель на буфер, где будет храниться оцифро- ванное изображение, программе низкого уровня для драйвера цифрового пре- образователя. Как только элементы изображения переводятся в цифровой код, они помещаются в буфер для изображений и происходит приращение указателя буфера. Фрагмент программы, который приводится ниже, показывает, как осу- ществляется контроль за пересечением границы сегмента и выполняется про- грамма коррекции. В этой программе ВР-регистр процессора содержит смеще- ние, а ES-регистр — сегментную часть указателя буфера для данных изображе- ния. Программа взята из файла digitize.asm. SEGOFF EQU (OFFFFH/1OH)+1 ;парахрафы/сегмент •.контролирует выход указателя для накопителя данных ; (ВР-регистр) за границы сегмента размером 64К ;байт. Если выход произошел, регистр сегмента ;должен быть отрегулирован с помощью SEGOFF так, ; чтобы смещение 0 в сегменте указывало на следующий ;из имеющихся байтов накопителя. срт bp.OFFFFH ;на границе сегмента? jne gpl4 ;если нет, то переход mov ax.es ;на границе сегмента - получает ;значение регистра сегмента. add ax,SEGOFF •.добавляет смещение mov es,ax ; возвращает назад ; смещение будет установлено на 0000 gpl4: inc bp ; сдвигает смещение на один байт Приведенная программа использует значительно меньший объем кода, чем процедура нормализации значения указателя. Распределение данных между кодом Си и ассемблера Учитывая, что прикладная программа (подобная представленной в книге про- грамме для цифрового преобразователя) требует использования кода как на языке Си, так и на языке ассемблера, мы уделим некоторое время обсуждению того, как информация распределяется между ними. Существуют два основных способа распределения информации:
Средства, разработки программного обеспечения 49 а) Ссылки данных на глобальные данные. б) Передача и возвращение параметров. Метод глобальных данных, хотя некоторые и не считают его лучшим для про- граммирования, является наиболее прямым путем распределения данных между кодами языков Си и ассемблера. Либо в коде Си, либо в коде ассемблера опре- деляют переменную, на которую можно ссылаться как на внешнюю в любом модуле программы, если к ней необходим доступ. Введение внешней перемен- ной позволяет без ошибок проводить компилирование или трансляцию с языка ассемблера. Процесс компоновки, который объединяет оба кода в исполняемую программу, разрешает ссылки на переменную, определенную в качестве внешней. Например, если в модуле кода на языке ассемблера определяется целая пере- менная MySum и на нее ссылаются, используя код Си, определение, размещенное в сегменте данных кода ассемблера, будет выглядеть следующим образом: PUBLIC .MySum .MySum DW 0 ; целая переменная требует одного машинного слова ; начальным значением MySum является ноль Для того чтобы из программы на Си сослаться на MySum, потребуется сле- дующее определение: extern int MySum; В свою очередь MySum можно было бы определить в коде Си и затем ссы- латься на нее из ассемблера. Определение на Си было бы таким: int MySum » 0; Определение на ассемблере записывалось бы следующим образом: EXTRN .MySum:WORD Внешнее определение делает переменную MySunl видимой в модуле, напи- санном на ассемблере. Для доступа к переменной можно было бы использовать следующую команду: mov ах,.Mysum Этот метод распределения данных не используется в программах, предста- вленных в книге, потому что, как оказалось, без него можно обойтись. Вместо этого используется метод передачи параметров, который обсуждается ниже. Метод передачи параметров — второй из методов распределения данных ме- жду кодом ассемблера и кодом Си. Параметры могут передаваться из кода Си функции (подпрограмме), написанной на языке ассемблера. В свою очередь, по- сле выполнения функция на языке ассемблера может вернуть результат в Си. Почти все программные разработки на Си используют стек для передачи пара- метров функциям. Для успешного взаимодействия Си и ассемблера необходимо знать размещение параметров в стеке. Достаточно понять формат стека, что- бы легко возвратить любой параметр, переданный из Си в ассемблер. Функция InitializeDigitizer, которая обсуждается в гл. 4, иллюстрирует взаимодействие между этими двумя кодами. 4-3
50 Глава 1. Общие сведения Язык Си в стандарте ANSI поддерживает два типа соглашений по передаче параметров: Си и Паскаль. Соглашение в формате Си используется всегда, по- ка не вводится в прототип функции ключевое слово Pascal. Если используется соглашение по передаче параметров в формате Си: а) Параметры помещаются в стек в направлении справа налево. б) Когда функция выполняется, она не удаляет параметры из стека. Эта ра- бота достается вызывающему коду. По этой причине использование согла- шения в формате Си может привести к увеличению памяти, занимаемой программой. в) Ко всем идентификаторам прибавляется спереди символ подчеркивания «\_». Обычно компиляторы предоставляют программисту возможность генерировать код без символа подчеркивания, если это необходимо; г) Может поддерживаться переменное число параметров. В свою очередь использование соглашений по передаче в формате языка Па- скаль означает: а) Параметры помещаются в стек в направлении слева направо. б) Когда выполняются функции и процедуры, они удаляют из стека все па- раметры. Это может привести к экономии памяти в большой программе. в) В качестве идентификаторов используются только символы верхнего ре- гистра и ведущий символ подчеркивания к ним не добавляется. г) Поддерживается только фиксированное число параметров. Соглашения по передаче в формате языка Паскаль, по-видимому, следует использовать при критическом заполнении программной памяти или когда не- обходим код Си для взаимодействия с функциями на языке ассемблера, которые были написаны первоначально для использования с языком Паскаль. В програм- мах, которые приведены в книге, используется только соглашение по вызовам в формате Си. Диаграмма, изображающая стек во время выполнения вызова функции на языке ассемблера, поможет наглядно представить механизм передачи параме- тров в формате Си. Предположим, например, что существует функция на языке ассемблера с именем Sum, которая вычисляет сумму двух чисел и возвращает результат. Прототип функции можно было бы записать в следующем виде: int Sum (int Numl, int Num2); Функция на языке ассемблера записывается так: PUBLIC _Sum _Sum proc near push bp mov bp,sp mov ax,[bp+4] ; получает Numl add ax, [bp+6] ; прибавляет Num2 pop bp ; восстанавливает bp ret ;сумма, возвращающаяся ;в регистр ах .Sum endp
Средства разработки программного обеспечения 51 Структура стека программы перед выполнением первой команды на языке ассемблера имеет следующий вид: по направлению к верхней памяти текущий регистр SP по направлению к нижней памяти Отметим следующее: а) Стек заполняется со стороны старших адресов памяти. б) Старший значащий байт любого значения типа WORD записывается в более высокий физический адрес памяти. При записи в стек указателей FAR смещение вводится в стек вслед за сегментом. в) Все стековые операции выполняются с 16-бит, 2-байт величинами. Любые значения байта или символа расширяются до 16 бит перед помещением в стек. г) Обычно при вызове функции в формате Си все параметры бывают до- ступны в виде положительных смещений относительно регистра базового указателя (ВР-регистра). Все автоматические и локальные переменные доступны в виде отрицательных смещений относительно ВР-регистра. д) Наличие в коде команды регистра базового указателя автоматически от- сылает к сегменту стека, если явно не указано обратное. Из диаграммы стека видно, что параметры, переданные функции ассемблера, действительно помещаются в стек в обратном порядке, причем сначала вводят- ся обе части Num2, а затем следуют обе части Numl. После того как параметры помещаются в стек, выполняется вызов функции Sum на языке ассемблера с адресацией типа NEAR, при этом адрес возврата этой функции помещается в 4*
52 Глава 1. Общие сведения стек над параметрами. Так как эта функция была определена как NEAR, тре- буется поместить в стек только смещение в текущем сегменте кода. Бели бы функция Sum находилась в другом сегменте кода и была поэтому определена как FAR, предыдущую диаграмму стека, необходимо было бы модифицировать следующим образом: Смещения фрейма стека по направлению к верхней памяти текущий регистр SP по направлению к нижней памяти В этом случае из-за адресации типа FAR было бы необходимо поместить в стек сегмент кода и смещение. После того как параметры и адрес возврата функции записаны в стек, в него помещают регистр базового указателя, чтобы сохранить его значение. При такой записи ВР-регистр может загружаться содер- жимым регистра указателя стека (SP-регистра). Это дает возможность доступа к параметрам, которые являются постоянным положительным смещением от- носительно ВР-регистра. ВР- и SP-регистры указывают на одну и ту же ячейку стека, показанную на диаграмме как смещение в стеке, равное 0. При загрузке SP-регистра в регистр ВР, доступ к параметрам становится тривиальным. Нуж- но просто рассчитать смещение между ВР-регистром и текущим параметром и использовать его вместе со значением регистра для доступа к параметру. Код на языке ассемблера, приведенный выше, показывает, как это делается. Когда завершается выполнение функции ассемблера, управление должно быть корректно передано функции Си. Для этого первоначальное значение ВР- регистра вынимается из стека и выполняется команда возврата. Тип этой ко- манды будет либо NEAR, либо FAR в зависимости от того, как была определена
Заключение 53 функция. В случае NEAR возвращается ячейка (смещение) в сегменте кода, с ко- торой следует продолжить выполнение программы, в регистр указателя коман- ды (IP) процессора. В случае FAR возвращаются как сегмент, так и смещение. В системе Turbo С используются различные методы для получения значений от функций. Метод определяется типом объекта, который необходимо возвра- тить. Функции ассемблера должны эмулировать эти методы, чтобы правильно возвращать значения программе, написанной на Си. Три таких метода предста- влены ниже: а) Возвращаемое значение помещается в АХ-регистр, если оно имеет тип char, short, int, enum или указателя типа NEAR. Другими словами, если тип может быть выражен 16-разрядным значением. б) 32-разрядные значения, которые включают указатели FAR и HUGE, воз- вращаются в пару регистров DX:AX. Старшие значащие 16 бит будут раз- мещены в DX-регистре, а младшие в АХ-регистре. в) Возвращение структур данных происходит так: структура помещается в область статических данных и возвращается указатель на нее. Для струк- тур типа NEAR указатель будет находиться в АХ-регистре, а для струк- тур типа FAR и HUGE он будет в паре регистров DX:AX. Процедура Sum помещает получающуюся в результате сумму параметров в АХ-регистр для возврата к функции Си. Сделаем последнее замечание по использованию функций ассемблера в про- граммах на языке Си. В большинстве случаев язык Си не предполагает, что реги- стры процессора будут сохраняться при вызовах функций. Это означает, что все регистры процессора, за исключением CS, SS, SP, BP, SI и DI, могут свободно ис- пользоваться функцией ассемблера без записи и восстановления. Кроме того, мо- гут использоваться SI- и DI-регистры, если исключается использование системой Turbo С регистровых переменных (через меню Option/Compiler/Optimization). В функции ассемблера могут использоваться все регистры пока значение, которое они имели на входе, восстанавливается на выходе. Это легко обеспечить, если по- местить регистры в стек, использовать их там по необходимости, а затем вернуть обратно до возврата в функцию Си. Заключение В первой части этой главы мы обсуждали потенциальные графические возмож- ности компьютеров семейства PC для обработки изображений. Подробно рас- сматривались режимы, разрешения и структуры палитры для адаптеров EGA и VGA. Затем описывалось на уровне битов действие принтерного порта, посколь- ку этот интерфейс будет использоваться цифровым преобразователем, который мы в дальнейшем будем разрабатывать. И наконец, мы увидели, что правильный выбор модели памяти компилятора, типов указателей данных, способов распре- деления данных является очень важным для эффективной работы программ об- работки изображений. В частности, эти атрибуты могут значительно повлиять на размер программы и ее скорость. В действительности, от них может зависеть, будет ли вообще работать цифровой преобразователь. Теперь мы можем сосредоточится на более интересных вопросах ввода и об- работки изображений.
Глава 2 Основы видеотехники В главе рассмотрены следующие вопросы: • Как изображения выводятся на ЭЛТ? • Как выглядит видеосигнал? • Какие типы «синхроимпульсов» определяют видеосигнал? • Какие видеостандарты используются в настоящее время? Введение Для того чтобы понять работу цифрового видеопреобразователя, который рас- сматривается в гл. 3, необходимо, во-первых, знать, что такое видеосигнал и, во- вторых, как видеосигналы отображаются на электронно-лучевой трубке (ЭЛТ). Ниже рассмотрены устройство и работа электронно-лучевых трубок, которые устанавливаются в компьютерных мониторах и в обычных телевизионных при- емниках. Чтобы образовать на ЭЛТ изображение, электронный луч перемещается вперед-назад и вверх-вниз по экрану. Электроны возбуждают частицы люмино- фора, нанесенные на внутреннюю поверхность экрана, и заставляют их светить- ся. Интенсивность свечения люминофора определяется скоростью бомбардиру- ющих его электронов, которая в свою очередь контролируется приложенным напряжением. Следует ожидать, что чем выше напряжение, тем интенсивнее свечение люминофора. Устойчивость возбужденного люминофора, т. е. длитель- ность его свечения, после того как электронный луч удаляется, производит впе- чатление, что изображение является статическим, хотя в действительности в каждый момент времени возбуждается на экране лишь одна точка. Когда элек- тронный луч останавливается, изображение постепенно исчезает. В монохромных ЭЛТ на экран наносится только один люминофор. При бом- бардировке электронами это вещество дает характерное свечение — белое, ян- тарное или зеленое — в зависимости от типа монитора. Если частица люмино- фора не возбуждается электронным лучом, она будет темной или черной. При правильной модуляции электронного луча изображение выводится на экран в полном диапазоне оттенков, от черного до белого. Монохромные ЭЛТ имеют единственный источник электронов, называемый электронной пушкой. Магнит- ные поля, приложенные к ЭЛТ, направляют электронный луч, созданный элек- тронной пушкой (с помощью магнитного отклонения), на частицы люминофора. Одновременно с этим интенсивность луча модулируется, чтобы получить различ- ные уровни яркости. Магнитные поля используются для развертки электронного луча по экрану ЭЛТ как в горизонтальном, так и в вертикальном направлении. Цветные ЭЛТ имеют три разновидности люминофора, которые при возбу- ждении дают красное, зеленое и синее свечение. Из этих трех первичных цветов
Введение 55 можно получить и воспроизвести на экране все другие цвета. Относительные ин- тенсивности первичных цветов определяют то, что глаз Воспринимает как цвет элемента изображения на экране. Частицы трех люминофоров наносятся плот- но упакованными в матрице на внутреннюю поверхность экрана. Цветные ЭЛТ обычно, хотя и не всегда, имеют три электронные пушки, причем каждая наво- дится так, чтобы возбуждать только частицы одного вида люминофора в матри- це. Контролируя интенсивность и расположение каждого из трех электронных лучей, можно получать на экране цветные изображения. На самом деле действие ЭЛТ значительно сложнее, чем может показаться из предыдущего рассмотрения. Чтобы разобраться в более тонких деталях того, как работают ЭЛТ, нужно иметь представление о видеосигналах, которые подво- дятся к ним. К сожалению, видеосигналы сами по себе тоже довольно сложны. Не забывайте, что для получения стабильного и четкого изображения нужен точный контроль положения и интенсивности электронного луча. Необходима синхронизация как в горизонтальном, так и в вертикальном направлениях. Неос- поримость этих утверждений станет очевидной, как только мы изучим и обсудим рис. 2.1. На этом рисунке наглядно представлено то, что называется чересстрочной (2:1) видеосистемой в стандарте NTSC. NTSC является одним из трех видео- стандартов, которые используются в настоящее время. Видеостандарты рассма- триваются в этой главе немного ниже. Из рисунка видно, что видеоизображение генерируется с помощью двух раз- верток электронного луча по экрану ЭЛТ. Каждую развертку называют полука- дром, при этом два полукадра составляют видеокадр. Частицы люминофора, ко- торые возбуждаются электронным лучом одного полукадра, «чередуются» или располагаются в шахматном порядке относительно точек другого полукадра. Один полный полукадр отображается перед тем, как начинается проявление другого полукадра. Отметим, что каждый полукадр содержит одну полустроку. Первый полукадр заканчивается на половине строки, тогда как второй полу- кадр начинается с половины строки. По существу, первый полукадр записывает четные строки развертки, а второй полукадр — нечетные. Пунктирные линии показывают моменты, когда электронный луч выключа- ется. Такое выключение необходимо в течение как кадрового, так и строчного обратного хода развертки, чтобы не произошло искажения изображения, пока электронный луч перемещается для следующей развертки экрана. Интервалы, в течение которых отсутствует луч, называются интервалами гашения. Кадровое гашение возникает в верхней и нижней части дисплея, а строчное гашение — в левой и правой части. Пытаясь извлечь информацию о видеоизображении, ко- торая требуется для перевода этого изображения в цифровой код, необходимо понимать, в каком месте видеосигнала появляются интервалы гашения. Видеоизображение в стандарте NTSC состоит из 525 горизонтальных строк видеоинформации, которая обновляется 30 раз в 1 с. Так как кадр появляется 30 раз за 1 с, каждый полукадр требует половины этого времени, или 1/60 с. По- скольку 525 строк поровну разделены между полукадрами, каждый полукадр включает 262.5 строки. Наличие дробной части в этом числе обусловливает су- ществование полустроки дисплея в обоих полукадрах видеоизображения. Вывод 262.5 строк видеоизображения за 1/60 с означает, что каждая строка требует
56 Глава 2. Основы видеотехники приблизительно 63.5 мкс. Это число является важным конструкционным пара- метром для преобразователя (гл. 3), поскольку оно соответствует промежутку времени между вертикально расположенными элементами изображения на дис- плее в стандарте NTSC. Не все 262.5 строки видеоинформации в полукадре применяются для вывода изображения. Приблизительно 18.5 строк используются для кадрового гашения и синхронизации дисплея. Для информации о видеоизображении остается по 244 строки на каждый полукадр. Эти 244 строки на один полукадр или 488 строк на кадр составляют изображение, которое мы видим на экране мониторов или те- левизоров. Из тех строк, которые используются для кадрового гашения, строки каждого полукадра от 17 до 20 можно использовать также для других целей. Применения этих скрытых видеострок включают тестирование и диагностику, введение вставок и контрольную передачу сигнала. С помощью этих по суще- ству неиспользуемых строк можно передавать полезную информацию, которая не связана с видеоизображением. Такую информацию можно декодировать с по- мощью соответствующим образом настроенного приемника, не воздействуя при этом на нормальный вывод изображения на экран. Очень важна синхронизация электронного луча. Информация о синхрониза- ции должна присутствовать в видеосигнале для: а) синхронизации строчного обратного хода развертки; б) синхронизации кадрового обратного хода развертки; в) кадрового гашения; г) строчного гашения.
Введение 57 Кроме того, требуется чтобы выводимое изображение определялось также амплитудой видеосигнала (для монохромного изображения) или цветовой ин- формацией. Видеосигналы, которые содержат одновременно информацию как о синхронизации, так и об изображении, называются композиционными видеосиг- налами. Монохромный композиционный видеосигнал в стандарте EIA (Electronic Industries Association), который нас более всего интересует, определяется специ- фикацией RS-170, которая накладывает ограничения на электрические харак- теристики видеосигнала таким образом, что он может передаваться между со- вместимым видеооборудованием без преобразования. Совместимые составные ви- деосигналы используются в большинстве телевизионных камер, в некоторых мо- ниторах для компьютеров, видеомагнитофонах и некоторых персональных ком- пьютерах. Следует отметить, что стандарт цветного телевидения NTSC (National Television Standards Committee) является надмножеством монохромного стандар- та RS-170 и определяется спецификацией RS-170A. На рис. 2.2 представлена диаграмма синхронизации для цветового видеосиг- нала в стандарте RS-170. Рисунок показывает отношение контрольной инфор- мации и информации об изображении в видеосигнале. Показана только синхро- низация: амплитуды видеосигнала будут рассмотрены ниже. Если проигнори- ровать пока цветовые признаки, то синхронизация совершается с помощью ко- ротких синхроимпульсов, отклоняющихся от уровня, который определяется как уровень гашения, к уровню синхронизации. Системы, подобные представленной на рисунке, с отрицательно идущим синхроимпульсом, называются системами с отрицательной синхронизацией. Можно предположить, что композиционный видеосигнал содержит как цифровые, так и аналоговые компоненты. Цифро- вая компонента состоит из различных синхроимпульсов, тогда как аналоговая компонента является действительным видеосигналом (показан на диаграмме в виде ступенчатой функции). Синхросигнал обеспечивает синхронизацию ЭЛТ с источником видеосигналов. Активная часть видеосигнала модулирует интен- сивность электронного луча во время его синхронной развертки по поверхности экрана дисплея. Модуляция заставляет частицы люминофора, по которым про- ходит электронный луч, светиться с интенсивностью, пропорциональной модули- рующему сигналу. При выделении из видеосигнала информации об изображении важно знать тип, ширину и величину синхроимпульсов. Можно определить три различных типа импульсов: 1. Строчный синхроимпульс, который имеет ширину 4.7 мкс. Этот импульс заставляет электронный луч перемещаться с правой стороны экрана назад на левую сторону, устанавливая его для вывода новой видеостроки. Во время этого строчного обратного хода развертки видеосигнал гасится, так что линия обратного хода на экране не видна. 2. Кадровый синхроимпульс, который имеет ширину 27.1 мкс. Этот импульс заставляет электронный луч перемещаться из нижней правой части экра- на назад в верхнюю левую часть, устанавливая его для вывода следую- щего полукадра. Чтобы сохранить синхронизацию дисплея с источником видеосигналов, во время кадрового обратного хода развертки продолжают появляться строчные синхроимпульсы. 3. Выравнивающий импульс с шириной 2.3 мкс. Этот импульс используется, чтобы поддержать уровень постоянного тока видеосигнала пока в дисплей подается информация по синхронизации.
FIELDS I & III TIME VERTICAL BLANKING INTERVAL * 20H**l 0 5M-*| FIELD H SYNC INTERVAL пгтттгиииииит FRAMEA: FIELDS I & II FRAME B: FIELDS III & IV DETAIL XX U------\---9 LINE VERTICALXINTERVAL-----\ i 1 1 k Гу ’ I । . 1 M PRE EQUALIZING VERTICAL SYNC | POST EQUALIZING PULSE INTERVAL PULSE INTERVAL PULSE INTERVAL REFERENCE SUBCARRIER PHASE, COLOR FIELDS I & III IRE 0— H -20- FIELDS II & IV START OF : FIELD > -40---- VERTICAL BLANKING INTERVAL------------------Ur-i r rf- 2.3 pS± 0.1 pS EQUALIZING PULSE 4.7 pS ♦0.1 pS VERTICAL SERRATION /> REFERENCE SUBCARRIER J PHASE, COLOR FIELDS II & IV Рис. 2.2. Синхронизация видеокадра. Примечание Диаграмма включает цветовую информацию. Для монохромного видеосигнала полукадры III и IV идентичны полукадрам I и II.
Введение 59 Видеоамплитуда - переменный аналоговый сигнал + 100 IRE Реперная белая линия Пиковая амплитуда - 1В 0 IRE Реперная черная линия -40 IRE Примечания 1. Нарастающий фронт 1,5 мкс 2. Горизонтальный синхроимпульс 4,7 мкс 3. Цветоразрсшение 8-10 циклов при 3,579545 Мгц 4. Ниспадающий фронт 4,7 мкс 5. Бланкирование изображения 11,0 мкс 6. Одна видеолиния 63,5 мкс 7. Активное время видеолинии 52-55 мкс 8. Аналоговый видеосигнал Рис. 2.3. Синхронизация видеостроки. Если подсчитать общее число отрицательных импульсов (строчный, кадро- вый и выравнивающий синхроимпульсы) в обоих полукадрах видеокадра в стан- дарте NTSC, то в результате получится, что первый полукадр содержит 272 импульса, а второй полукадр — 271 импульс. Эти числа важны, когда компью- тер пытается анализировать видеосигнал во время его поступления. Рассчитав число импульсов в полукадре, программа может определить, какой полукадр является первым, а какой — второй. Цифровой преобразователь, представлен- ный в гл. 3, будет использовать этот метод расчета при переводе чересстрочного видеосигнала в цифровой код. На рис. 2.3 показана одна строка композиционного видеосигнала, которая со- держит как видеоинформацию, так и информацию о синхронизации. На рисунке отмечены различные компоненты видеосигнала и даны приблизительные вре- менные интервалы. Из 63.5 мкс приблизительно 53 мкс отводятся на передачу информации об изображении. Остальное время уходит на синхронизацию сигна- ла. Отметим, что видеосигнал постоянно изменяется в интервале между двумя уровнями, обозначенными как белый и черный. Амплитуда переменных сигналов в стандарте RS-170 определяется в едини- цах IRE (Institute of Radio Engineers). Стандартный видеосигнал с двойной ам- плитудой 1.0 В разбивается на 140 IRE. Коэффициент преобразования составля- ет 7.1429 мВ на IRE. Все уровни напряжения между —40 IRE (синхроуровень) и нулем (уровень гашения) считаются управляющими компонентами видеосиг- нала или компонентами синхронизации. Уровни напряжения между +7.5 IRE и +100 IRE содержат действительную видеоинформацию. Скачок напряжения до +100 IRE (относительно синхроуровня) приводит к отображению на экране бе- лой точки (элемента изображения). При напряжении 0.34 В или 7.5 IRE элемент изображения будет черным. Значения напряжения в диапазоне между этими предельными уровнями приводят к появлению элемента изображения промежу- точной интенсивности. При напряжениях ниже уровня 0.34 В электронный луч
60 Глава 2. Основы видеотехники выключается или гасится. Этот уровень напряжения иногда называют уровнем ниже черного. Все измерения амплитуды составного видеосигнала предполагают полное со- противление 75 Ом. Спецификация RS-170 определяет, что полное сопротивле- ние видеоустройства в частотном диапазоне от 0 до 4,5 МГц должно составлять 75 Ом. Входное полное сопротивление является важным конструкционным па- раметром для преобразователя, рассмотренного в гл. 3. Чтобы соответствовать стандарту RS-170, видеосистема должна иметь раз- решение по крайней мере 330 линий в вертикальном направлении и 525 линий в горизонтальном направлении. Разрешение — это мера «способности» системы проявить мелкие детали видеоизображения. Большинство современных видео- камер и дисплеев с ЭЛТ соответствуют этим требованиям. Однако большинство видеомагнитофонов не обеспечивают необходимого разрешения по вертикальным линиям. Формат SuperVHS является исключением, поскольку дает более 400 вертикальных линий. Это приводит к более высокому качеству изображения. В общем случае все сравнения видеоустройств между собой следует проводить с точки зрения разрешения по вертикальным линиям, поскольку число гори- зонтальных линий фиксируется видеостандартом. Чем выше разрешение по вер- тикальным линиям, тем качественнее получаемое изображение. К сожалению, видеоустройства с более высоким разрешением имеют также и более высокие цены. Видеостандарты До сих пор мы рассматривали только стандарт NTSC и монохромный компози- ционный видеосигнал в этом стандарте (табл. 2.1). В действительности, широ- ко используются три стандарта цветного телевидения: NTSC, PAL и SEC AM. Стандарты PAL и SECAM не используются в Соединенных Штатах, и поэтому мы лишь вкратце рассмотрим их в этом разделе. Также только поверхностно будут рассмотрены схемы цветового кодирования, поскольку прямое выделе- ние цветовой видеоинформации не поддерживается преобразователем, описан- ным в следующей главе. Дополнительную информацию по методам цветового кодирования можно найти в статьях и книгах, приведенных в разделе «Литера- тура». Стандарт NTSC NTSC является стандартом, которому соответствуют все находящиеся под кон- тролем правительства системы цветного телевидения в Соединенных Штатах Америки. Этот стандарт, используемый также в Японии и частично в Южной Америке, был принят в Соединенных Штатах в 1953 г. Два других стандарта цветового кодирования были разработаны позже. Обсуждение методов цветово- го кодирования, которое приводится ниже, взято из «Руководства по растровой графике» CONRAC и спецификации RS-170. Полную информацию об этом спра- вочнике вы найдете в разделе «Литература». Основным принципом, лежащим в основе всех трех стандартов цветового ко- дирования, является слияние двух отдельных передач изображения, широкопо-
Видеостандарты 61 Таблица 2.1. Краткое представление спецификации стандарта цветного телевидения NTSC Параметр видеосистемы Спецификация Ширина полосы видеосигнала Частота строчного синхроимпульса Частота кадрового синхроимпульса Полное сопротивление видеоустройства Уровень стандартного видеосигнала 0-4.5 МГц 15750 Гц (полоса), 15734 Гц цвет 60 Гц (полоса), 59.94 цвет 75 Ом, 0-4.5 МГц Двойная амплитуда 1.0 В Контрольные уровни Контрольный уровень белого +100 IRE или 1,00 В выше синхроуровня Контрольный уровень черного +7,5 IRE или 0,34 В выше синхроуровня Уровень гашения 0,0 IRE или 0,286 В выше синхроуровня Синхронизация —40 IRE или 0,0 В лосного сигнала, несущего информацию по яркости, и более узкого сигнала цвет- ности. Последний добавляется к сигналу яркости в виде модулированной подне- сущей. В стандарте NTSC используется частота поднесущей сигнала цветности 3,579545 МГц, в то время как стандарты PAL и SECAM используют частоту 4,2 или 4,4 МГц. Три сигнала с информацией по первичным цветам корректируются с помо- щью гамма-фактора (см. гл. 1). Затем генерируется сигнал яркости как сумма трех долей цветового сигнала, которые устанавливаются по их относительным вкладам в эталонную яркость «белого цвета». Компонента цветности получает- ся вычитанием сигнала яркости из цветовых сигналов, чтобы получить цвето- разностные значения: красный минус яркость, зеленый минус яркость и синий минус яркость. Поскольку яркость также передается как сумма цветовых до- лей, только два из этих цветоразностных сигналов должны передаваться, чтобы обеспечить полное определение всех трех первичных цветов. В системе NTSC цветоразностные сигналы взвешиваются, дешифруются и фильтруются, чтобы дать широкополосный сигнал (в фазе) оранжевый/циано- вый I и узкополосный сигнал (сдвинутый по фазе на 90 градусов) малиновый/зе- леный Q. Эти два сигнала затем используются для модуляции двух поднесущих сигналов с частотой 3,58 МГц, которые между собой сдвинуты по фазе на 90 гра- дусов. Модулированные поднесущие сигналы также сдвинуты по фазе на —57 и —147 градусов от поднесущего сигнала с опорной частотой, который сопровожда- ет каждый строчный синхроимпульс (см. рис. 2.3). При выборе ширины полос сигналов I и Q и характерных цветов учитывается тот факт, что зрительная система человека может воспринимать не все цвета в малых деталях и, кроме того, имеет меньшую способность разрешать детали малинового и зеленого цвета по сравнению с деталями оранжевого и цианово- го цвета. Ширина полосы сигнала Q поэтому поддерживается равной 0,5 МГц. Ширина сигнала I приблизительно в три раза больше (1,3 МГц), однако толь- ко компоненты нижней боковой полосы удерживаются за пределами сигнала Q с двойной боковой полосой. Когда два поднесущих сигнала комбинируются,
62 Глава 2. Основы видеотехники результирующая фаза является прямым аналогом цвета, в то время как ампли- туда комбинированного сигнала будет косвенной мерой цветовой насыщенности. Таким образом, нулевая амплитуда поднесущей соответствует нулевой цветона- сыщенности, что приводит к монохромному черному или белому изображению, которое целиком контролируется величиной сигнала яркости. Между сигнала- ми с монохромной и цветовой кодировкой существуют также различия в синхро- низации. Эти различия сделаны достаточно малыми, так чтобы монохромные приемники и мониторы могли сохранять синхронизацию, даже если принимают сигнал с цветовой кодировкой. Стандарты PAL и SECAM Стандарты PAL и SECAM разработаны для систем, которые работают с 625 стро- ками на кадр и 25 кадрами в 1 с. Оба стандарта построены так, чтобы избежать проблем искажения цвета, которые могут возникать в системах стандарта NTSC. Стандарты PAL (Phase Alternation Line) разрабатывались фирмой Telefunken и сейчас широко используются в Европе. Система цветного телевидения SECAM (Systeme Electronique Couleur avec Memoire) широко используется во Франции, СНГ и во всех восточно-европейских странах. Заключение В этой главе рассмотрены основы видеотехники на примере того, как использу- ется электронный луч для создания изображения на экране ЭЛТ. В основном рассматривались монохромные видеосигналы, хотя частично были затронуты и цветовые видеосигналы. Особое внимание было уделено типу, числу и продол- жительности различных синхроимпульсов, присутствующих в видеосигнале. Эти синхроимпульсы играют основную роль в разработке цифрового видеопреобра- зователя, который обсуждается в гл. 3.
Глава 3 Аппаратные средства цифрового видеопреобразователя В главе рассмотрены следующие вопросы: ♦ Возможности коммерческих цифровых видеопреобразователей. ♦ Технические характеристики цифрового видеопреобразователя. ♦ Интерфейс компьютер/преобразователь. ♦ Работа аналоговой и цифровой частей видеопреобразователя. ♦ Монтаж и проверка цифрового видеопреобразователя. Введение Изучив основы видеотехники, которые были даны в предыдущей главе, мы мо- жем теперь обсудить проект и создание аппаратуры, которая может переводить видеоизображения в цифровой код и позволяет импортировать их в компьютер семейства PC для обработки и вывода на экран монитора. В некотором смысле функцией цифрового видеопреобразователя является удаление из композицион- ного видеосигнала части, которая обеспечивает синхронизацию, перед преобра- зованием амплитуды аналогового видеосигнала в его цифровое представление. Видеопреобразователь и/или компьютер должны иметь достаточно интеллекта, чтобы понимать видеосигналы для правильного выделения и интерпретации ин- формации об изображении. Для накопления данных изображения и обработки, которая, возможно, будет выполняться, должен быть предусмотрен интерфейс к компьютеру. Поскольку выпускаемые цифровые видеопреобразователи имеют разные фор- мы, размеры и функции, мы должны выбрать терминологию, которую будем ис- пользовать для описания каждого типа и которая опирается на их функциональ- ные различия. В этом смысле самыми важными характеристиками являются: а) возможности покадровой оцифровки в сравнении с оцифровкой по полу- кадрам; б) возможности оцифровки стоп-кадра в сравнении с оцифровкой в реальном времени; в) разрешение; г) размер растра образца; д) возможности работы с монохромным и цветным изображением; е) возможности обработки изображений в самом видеопреобразователе; ж) возможности видеовыхода.
Рис. 3.1. Блок-схема полномасштабного цифрового видеопреобразователя.
Введение 65 Рис. 3.2. Блок-схема цифрового видеопреобразователя стоп-кадра. Блок-схему типичного полномасштабного цифрового видеопреобразователя можно было бы представить в том виде, как показано на рис. 3.1. Блок-схема видеопреобразователя, рассмотренного в этой главе, приводится для сравнения на рис. 3.2. Видеопреобразователи, называемые также ловушками кадров, позволяют оцифровывать видеосигнал в реальном времени и, кроме того, могут оцифро- вывать оба полукадра чересстрочного видеокадра. Ловушки полукадров имеют аналогичные характеристики, за исключением того, что они игнорируют один из полукадров, уменьшая поэтому на половину возможное разрешение. Видеопре- образователи, работающие в реальном времени, допускают перемещение (хотя и медленное) объекта, на который направлена видеокамера, без искажения оци- фрованного изображения. Это возможно в том случае, когда полный видеокадр может быть оцифрован менее чем за 1/30 с и объект за время оцифровки не перемещается быстро. С помощью видеопреобразователя, работающего в реаль- ном времени, возможна оцифровка телевизионных сигналов и сигналов от ви- деомагнитофонов, поскольку такой видеопреобразователь может поддерживать требуемую скорость видеоизображения. Видеопреобразователи стоп-кадра рас- считаны на то, что видеоизображение является стационарным во время процес- са оцифровки. Любое движение объекта приведет к искажению оцифрованного изображения. Разрешение определяет количество элементов изображения, которое видео- преобразователь способен обработать. Видеопреобразователи, работающие в ре- альном времени, обычно используют квадратную матрицу элементов изображе- ния 256 х 256 или 512 х 512. Тогда изображение будет состоять из расположенных друг над другом 256 горизонтальных линий, включающих по 256 элементов ка- ждая. Эти числа выбраны в качестве размеров матрицы вследствие требований, предъявляемых к памяти видеопреобразователя. Емкость любых микросхем па- 5-3
66 Глава 3. Аппаратные средства цифрового видеопреобразователя мяти представляет собой степень числа два. Некоторые видеопреобразователи стоп-кадра имеют разрешения, которые не связаны со степенью числа два, так как они не содержат памяти для записи изображений. Вместо этого они ис- пользуют память компьютера и потому не ограничены размером памяти самого видеопреобразователя. Видеопреобразователи, а также адаптеры дисплея имеют фиксированное чи- сло разрядов, которые используются для представления образца (или элемента) изображения. Каждый образец представляет интенсивность изображения в том месте, где этот образец был оцифрован. Чем большим числом разрядов предста- влен образец, тем точнее цифровое представление изображения. Число разрядов, которое поддерживается, определяет стоимость видеопреобразователя, посколь- ку при увеличении числа разрядов увеличиваются размеры необходимой памяти и аналогово-цифрового преобразователя (АЦП). Обычно образец представляет- ся в четырех, шести или восьми разрядах, что дает в результате 16, 64 или 256 возможных значений интенсивности образца. Очевидно, что если каждый элемент изображения может принимать любое из 256 значений, он будет более точно представлять оригинальное изображение, чем образец, значения которого ограничиваются только 16 возможностями. Цифровые видеопреобразователи могут быть не только монохромными, но и предоставлять возможности для вывода цветных изображений. Если видео- преобразователь поддерживает цвет, он имеет набор из трех аналого-цифровых преобразователей и трех видов памяти: для красной, зеленой и синей ком- понент цветного изображения. Для каждого элемента изображения выполня- ются три ряда оцифровок. Оцифровки могут выполняться параллельно (од- новременно) или последовательно (одна после другой). Монохромные видео- преобразователи имеют один АЦП и, возможно, одну память для хранения оцифрованного изображения. Для вывода на экран изображений, созданных монохромным видеопреобразователем, используются различные оттенки серо- го цвета, чтобы представить все возможные значения интенсивности элемен- та. Такие изображения называются полутоновыми изображениями, поскольку для вывода на экран используется полный диапазон оттенков от черного до белого. Наличие или отсутствие собственного процессора для обработки изображе- ний является другой характерной чертой, по которой может отличаться один видеопреобразователь от другого. При наличии собственного процессора раз- личные алгоритмы обработки изображений могут выполняться непосредственно на видеопреобразователе, не загружая компьютер задачами с большим количе- ством вычислений. Кроме того, эти алгоритмы обработки изображений могли бы обеспечиваться аппаратными средствами, а не программным обеспечением. Это позволит уменьшить на порядок время обработки изображений. Время вы- игрывается также за счет того, что данные изображения не будут в этом случае передаваться между видеопреобразователем и компьютером. И наконец, многие видеопреобразователи имеют видеовыход, который исполь- зуется для управления видеомонитором. Такое управление используется для просмотра действий алгоритма обработки изображения во времени, близком к реальному, или просто для точной настройки видеопреобразователя (т. е. пози- ционирования изображения).
Введение 67 Зная возможности коммерческих видеопреобразователей, вы, возможно, ра- зочаровались бы, увидев характеристики видеопреобразователя, описанного ни- же. В этом видеопреобразователе для уменьшения его стоимости не используют- ся излишества, которые характерны для коммерческих видеопреобразователей. Наша цель — способствовать развитию методов обработки изображений, кото- рые широко представлены в книге. Вы можете разработать собственные при- кладные программы быстрее, имея под рукой уже отработанные методы. Наш видеопреобразователь позволит подключить большее число людей к этому ви- ду экспериментирования. Учитывая это, приведем следующие характеристики видеопреобразователя: 1. Возможность ввода по кадрам или полукадрам от любого источника ви- деосигналов в стандарте RS-170 (с одним условием, см. п. 6). 2. С небольшим изменением аппаратура видеопреобразователя будет рабо- тать со стандартами цветного телевидения PAL либо SECAM. Она под- держивает по умолчанию монохромный стандарт NTSC. 3. Поддерживает разрешения 320 х 200, 640 х 200 и 640 х 480. 4. Шестиразрядный аналого-цифровой преобразователь, дающий 64 различ- ных уровня интенсивности. 5. Видеопреобразователь может непосредственно оцифровывать монохром- ные изображения. Цветные изображения требуют дополнительной про- граммной поддержки (описана в гл. 5). Можно использовать источники цветовых видеосигналов, однако получающиеся в результате изображения будут только монохромными. Информация о цвете удаляется из состав- ного видеосигнала перед процессом оцифровки. 6. В цифровой код могут переводиться только неподвижные видеоизображе- ния. 7. Собственный видеопроцессор отсутствует — вся обработка совершается с помощью подсоединенного компьютера IBM PC или совместимого с ним. 8. Видеопреобразователь является самостоятельной частью аппаратуры и не требует платы расширения внутри PC. Его можно присоединить к любому компьютеру, который поддерживает Cetronics-совместимый принтерный порт. 9. Видеовыход не обеспечен — изображения выводятся только на компью- тер. 10. Видеопреобразователь будет стоить не более 50 долл. Программы, представленные в следующих двух главах для компьютеров IBM PC или совместимых с ними, обеспечивают: а) Получение и вывод на экран монитора монохромных изображений с теми разрешениями, которые указаны выше. Поддерживаются как чересстроч- ный, так и нечересстрочный режимы приема. Для вывода изображений на экран монитора VGA используется 16 или 64 уровня яркости. б) Окно фокуса, обновляющееся во времени, близком к реальному, и исполь- зуемое для наведения и фокусировки подключенной видеокамеры.
68 Глава 3. Аппаратные средства цифрового видеопреобразователя в) Специальный режим с 256 цветами, который с помощью набора цветных светофильтров может давать цветные изображения при использовании монохромной видеокамеры. Полученные цветные изображения могут за- писываться в специальном формате image-файлов, которые будут испол- нимыми, подобно стандартному COM-файлу системы MS-DOS. При вы- полнении image-файла изображения будут непосредственно выводиться на экран дисплея VGA. г) Возможность записывать оцифрованные изображения в стандартных гра- фических форматах (PCX или TIFF). При этом изображения могут им- портироваться другими прикладными программами (т. е. программами ри- сования и электронной верстки). Предусмотрена специальная программа просмотра, для того чтобы изображения, записанные в этих графических форматах, могли легко выводиться на экран. Сборка видеопреобразователя не представляет трудностей. Если ничего не менять в исходном проекте, то любой, кто имеет некоторый опыт монтажа элек- трических схем, не будет иметь проблем со сборкой. При использовании методов монтажа накруткой, видеопреобразователь можно полностью собрать прибли- зительно за один день. Если же он монтируется на печатной плате, вся сборка займет около часа. Большинство деталей — это стандартные интегральные схемы ТТЛ LS, име- ющиеся в продаже практически в любых магазинах по электронике. Единствен- ными в некоторой мере специализированными элементами видеопреобразователя являются аналогово-цифровой преобразователь СА3306С и кварцевый генератор с частотой 25,0 МГц (можно было бы использовать любой генератор в диапазоне частот от 12,38 до 12,5 МГц. В этом случае единственным изменением в общей схеме будет удаление схемы деления на два, помещенной непосредственно за тактовым генератором). Эти элементы можно приобрести у наиболее крупных поставщиков электроники или заказать по почте, воспользовавшись рекламой в журналах «Radio Electronics» или «BYTE». Для настройки собранного видеопреобразователя необходимо использовать два типа регулировки. Это связано с тем, что хотя видеопреобразователь явля- ется цифровым прибором, он имеет в своем составе аналоговую схему. Для про- верки собранного видеопреобразователя можно использовать только тестер, хотя желательно иметь и осциллограф. При правильном монтаже видеопреобразова- тель должен служить долго и надежно. Интерфейс компьютер/видеопреобразователь Видеопреобразователь подключается к компьютеру через Centronix-совмести- мый принтерный порт. Связь через принтерный порт для видеопреобразовате- ля была выбрана вместо последовательной связи или внутренней интерфейсной карты по следующим причинам: 1. При параллельном соединении по сравнению с последовательным возмож- ны более высокие скорости передачи данных.
Интерфейс компьютер/видеопреобразователь 69 2. Для первых компьютеров семейства PC и новых PC PS/2 потребовались бы отдельные интерфейсные платы. Для их установки пришлось бы ис- пользовать дополнительное гнездо на системной плате компьютера. 3. Принтерный порт Centronix на компьютерах IBM PC и совместимых с ними стал промышленным стандартом. 4. Принтерный интерфейс был наиболее совместимым среди компьютеров семейства PC. (Смотрите обсуждение, которое приведено ниже.) 5. Параллельная связь, обеспеченная принтерным портом, требует меньшего количества схем. Тем не менее решение использовать в качестве интерфейса принтерный порт было принято не сразу. Как вы, возможно, знаете, на серии компьютеров IBM PC, предшествующей PS/2, принтерный порт не был истинным двунаправлен- ным восьмиразрядным параллельным портом. Он имел в сумме двенадцать вы- ходных и пять входных ТТЛ-совместимых линий. Выходные линии использова- лись для передачи принтеру управляющих данных и данных программы печати, в то время как входные линии использовались для получения от принтера дан- ных о состоянии. При подключении принтерного порта к видеопреобразователю функции входных и выходных линий, разумеется, изменяются так, как этого требует видеопреобразователь. Одной из самых трудных задач при разработке видеопреобразователя бы- ла задача научиться передавать оцифрованные данные изображения и всю ин- формацию по синхронизации от видеопреобразователя к компьютеру, используя только пять имеющихся входных линий. Дело в том, что аналогово-цифровой преобразователь был шестиразрядным и, кроме того, требовалось по меньшей мере три бит состояния, поэтому, чтобы сделать входные линии компьютера мно- гофункциональными, был необходим хороший расчет. Разумеется, видеопреобра- зователь мог бы быть построен для работы только с более новыми компьютерами PS/2 или с компьютерами PC с истинным двунаправленным параллельным пор- том, однако было важно заставить его работать с наибольшим возможным чи- слом компьютеров PC различного уровня. В результате видеопреобразователь может подключаться через принтерный порт к любому компьютеру PC от ори- гинального до PS/2 модели 70/80. Как это было достигнуто, будет рассмотрено ниже. На рис. 3.3 показаны имена, присвоенные сигналам, с помощью которых ор- ганизуется взаимодействие видеопреобразователя и компьютера, а также даны направления этих сигналов и номера контактов. Видеопреобразователь подклю- чается к принтерному порту PC через кабель с 25 выходными контактами, подоб- ный экранированному кабелю RS-232. Кабель может иметь длину до 2 м и дол- жен быть экранирован. Ленточный кабель может быть использован при меньшей длине. (Если видеопреобразователь придется все-таки использовать с коробкой переключения, следует взять как можно более короткий кабель и самую со- вершенную коробку переключения.) Блок-схема видеопреобразователя показана на рис. 3.2. Электрическая схема приведена на рис. 3.4, а список материалов в табл. 3.1. К этим диаграммам мы будем обращаться при обсуждении последую- щего материала.
70 Глава 3. Аппаратные средства цифрового видеопреобразователя Видеопреобразователь Наименования сигналов Номера . — PC Наименования видеопреобразователя контактов Направление сигналов PC Strobe —Strobe Pixel Count DO 2 VW ►Data Bit 0 з •►Data Bit 1 Pixel Count D2 4 — Т1/0Ш WH 1 -►Data Bit 2 Pixel Count D3 5 ►Data Bit 3 Pixel Count D4 5 ►Data Bit 4 Pixel Count D5 7 -►Data Bit 5 Pixel Count D6 з -►Data Bit 6 Pixel Count D7 9 ▼IZ0HCI ин v -►Data Bit 7 Pixel Data Out D2 10 Twine UH f -Acknowledge Pixel Data Out D3 11 -►Busy Pixel Data Out D1 12 +P.End (out of paper) Pixel Data Out DO 13 ♦Select Sync Reset 14 -Auto Feed SyncEOC 15 -Error Go 1в —Init Printer High/Low 17 -Select Input Ground 18 Ground Ground 19 Ground Ground 20 Ground Ground 21 Ground Ground 22 Ground Ground 23 Ground Ground 24 Ground Ground 25 Ground Рис. 3.3. Схема передачи сигналов между видеопреобразователем и основным PC. Примечания 1. Все сигналы ТТЛ-совместимы. 2. Экранированный кабель RS-232 работает очень хорошо. Кабель видеопреобразовате- ля: экранированный кабель (75 Ом, RG-II, RG-59 или эквивалентный) длиной до 2 м с двумя охватываемыми разъемами типа BNC. Аналоговая часть видеопреобразователя Лишь малая часть схемы видеопреобразователя является аналоговой. Эта схема представляет собой модифицированную версию схемы, впервые опубликованной Стивеном Сайэрсиа в июньском выпуске журнала «BYTE» за 1986 год. Здесь она приводится с разрешения Стивена Сайэрсиа, которому принадлежат автор- ские права. Те, кому интересно изучить оригинальный вариант, могут найти его в издании Ciarcia’s Circuit Cellar, Vol. VII (McGraw-Hill, 1990). Функции, выпол- няемые этой схемой, включают: а) оконечную нагрузку 75 Ом для видеосигнала; б) коррекцию уровня постоянного тока видеосигнала; в) подавление цветовой поднесущей; г) буферирование сигнала; д) генерирование опорного напряжения АЦП.
Рис. 3.4. Электрическая схема видеопреобразователя.
72 Глава 3. Аппаратные средства цифрового видеопреобразователя Таблица 3.1. Список материалов для видеопреобразователя стоп-кадра Полупроводники Номер Количество Обозначение Деталь 1 1 D1 1N4148 2 1 D2 Любой светодиод 3 3 QI, Q2, Q3 2Т4401 4 1 IC1 Компаратор LM311 5 1 IC2 АЦП СА3306С 6 1 IC3 74LS157 7 1 IC4 74LS08 8 4 IC5, IC6, IC13, IC17 74LS74 9 1 IC7 74LS02 10 1 IC8 74ALS74 11 1 IC9 74LS11 12 3 IC10, IC11, IC12 74LS191 13 2 IC14, IC15 74LS373 14 1 IC16 Регулятор напряжения LM340T 5 В пост, тока 15 1 IC18 74LS32 16 1 IC19 74LS240 17 1 OSC Кварцевый генератор 25,0 МГц Пассивные элементы 18 6 С2, СЗ, С4, С5, С9, С10 47 пФ, все конденсаторы на 25 ВтВ пост, тока 19 10 С1, С6, С7, 08, 012, С14, 0,1 мкФ или больше С15, С16, 017, 018 20 1 С13 22 мкФ 21 1 СИ 100 мкФ 22 1 R1 75 Ом 5% 23 4 R2, R4, R6, R7 R3, R16 1к0м 5% 24 2 270 Ом 5% 25 5 R5, RIO, Rll, R20, R21 100 Ом 5% 26 2 R8, R15 39 Ом 5% 27 1 R9 10 кОм 5% 28 1 R12 150 Ом 5% 29 2 R13, R17 200, потенциометры триммера 120 Ом 5% 30 1 R14 31 1 R18 12 Ом 5% 32 1 R19 330 Ом 5% 33 1 L1 20 нГн, катушка индуктивности Гнездо разъема видеовыхода 34 1 Л типа BNC 35 1 J2 Вкладыш разъема DB25 36 1 J3 Штепсельный разъем для входа 9 В пост, тока 37 1 па Плата для монтажа накруткой или печатная плата 38 1 па Радиатор охлаждения для IC16 39 1 па Трансформатор 9 В пост, тока
Аналоговая часть видеопреобразователя 73 Резистор R1 на электрической схеме обеспечивает оконечную нагрузку 75 Ом для видеосигнала. Он требуется для согласования сопротивления с источником видеосигналов. Его можно удалить, если монитор параллельно присоединен к входу видеопреобразователя и обеспечивает надлежащую нагрузку. Конденсатор С1, резисторы R3 и R4 и диод D1 сдвигают уровень посто- янного тока входного видеосигнала к уровню, необходимому для аналоговой схемы. Связь видеосигнала по переменному току обеспечивается конденсато- ром С1. Связь по переменному току необходима, чтобы исключить смещение постоянного тока, которое могло бы возникнуть в подключенном источнике ви- деосигналов. При наличии связи по переменному току синхроуровень видеосиг- нала изменяется приблизительно на —0,286 В. Делитель напряжения, обра- зованный резисторами R1 и R2, дает на выходе напряжение приблизительно 1,06 В. Это напряжение, связанное с падением напряжения от 0,6 до 0,7 В на диоде с прямым смещением D1, сдвигает видеосигнал только на 0,4 В, чтобы довести отрицательно идущее отклонение синхроимпульса чуть выше уровня нуля. После сдвига видеосигнала к скорректированному уровню опорная частота цветовой поднесущей или частота цветовой насыщенности может быть отфиль- трована. Это необходимо только в том случае, когда на видеопреобразователь поступает цветовой видеосигнал. Такая фильтрация выполняется селекторным фильтром, в состав которого входят конденсаторы С1 и С2, катушка индуктив- ности L1 и резистор R5. Фильтр (его характеристики показаны на электрической схеме) подавляет опорную частоту цветовой поднесущей так, чтобы наличие этой частоты не влияло на качество оцифрованного изображения. По существу этот фильтр преобразует цветовой видеосигнал в монохромный. При использовании монохромного видеоисточника составляющие фильтра можно и нужно полно- стью удалить из электрической схемы. Другими словами, составляющие Cl, С2, L1 и R5 можно в эту схему не включать. Если «ловушка цветовой насыщенно- сти» не используется, убедитесь, что резистор R5 соединен с базой транзистора Q1. В противном случае схема работать не будет. При использовании источников видеосигналов в стандарте PAL или SECAM индуктивность катушки L1 следует соответственно изменить. Транзистор Q1 является повторителем напряжения и действует как буфер перед схемой, которая следует за ним. После того как видеосигнал буферирует- ся, он поступает на компаратор IC1 и на аналогово-цифровой преобразователь IC2. Изменения в цепи компаратора фиксируют те моменты, когда в видеосиг- нале появляется синхроимпульс. На выходе компаратора обеспечивается ТТЛ- совместимый видеосигнал, который поступает в цифровую часть электрической цепи видеопреобразователя. Уровень выходного сигнала снижается, как только снижается уровень синхроимпульса, причем длительность выходного импульса равна длительности входного синхроимпульса. К компаратору подводится по- стоянное опорное напряжение 200 мВ. Буферированный видеосигнал поступает на входной контакт аналогово- цифрового преобразователя. Дополнительная аналоговая схема генерирует уров- ни опорного напряжения, необходимые для преобразователя. АЦП является пре- образователем типа Flash. Это означает, что он имеет компараторы для каждого входного уровня, который будет преобразовываться, и внутреннюю резисторную
74 Глава 3, Аппаратные средства цифрового видеопреобразователя цепь для установки контрольных уровней для каждого компаратора. В таком шестиразрядном аналогово-цифровом преобразователе имеется всего 64 компа- ратора. Соединяя один конец резисторной цепи с уровнем напряжения, соответ- ствующим уровню черного (эмиттер Q3), а другой — с уровнем белого (эмиттер Q2), мы можем приписать любому напряжению в этом диапазоне одно из 64 воз- можных цифровых значений. Таким образом, уровню амплитуды видеосигнала приписывается некоторое число. Это число поступает от видеопреобразователя в компьютер для каждого переводимого в цифровой код элемента изображения. Схема, описанная в следующем разделе, управляет действием АЦП. (Если потре- буется более шести разрядов для получения более высокого разрешения, шести- разрядный АЦП (СА3306С) можно заменить на восьмиразрядный (СА3318С). В этом случае будут необходимы лишь незначительные изменения в схеме генера- тора опорного напряжения и в цепи мультиплексирования выходных данных.) Восьмиразрядный АЦП не использовался в первоначальной конструкции из-за его более высокой стоимости. Кроме того, дополнительные разряды для разре- шения по интенсивности, которые предоставляются восьмиразрядным преобра- зователем, совсем не просто использовать в программном обеспечении видеопре- образователя. Генераторы опорного напряжения представляют собой регулируемые, низко- омные источники постоянного напряжения, которые дают стабильные опорные уровни. Уровень напряжения, соответствующий уровню черного, настраивается приблизительно на 0,34 В, при этом уровню белого будет соответствовать 1,0 В. Отметим, что настройку генераторов опорного напряжения видеопреобразовате- ля следует производить лишь однажды на этапе его создания. Другая аналоговая схема представляет собой источник питания. Для видео- преобразователя таким источником является источник постоянного внешнего напряжения 9 В, который питается непосредственно от сети переменного на- пряжения 110 В. Напряжение 9 В снижается до 5 В, как этого требует стандарт ТТЛ. Используется регулятор напряжения LM340T или эквивалентный ему. По- ложительное напряжение 5 В является единственным, которое требуется для ви- деопреобразователя. Для снижения полного сопротивления источника питания в электрических цепях видеопреобразователя устанавливаются блокировочные конденсаторы (для предотвращения распространения в цепи высокочастотных помех). Регулятор напряжения должен иметь радиатор для обеспечения надле- жащего теплоотвода. Цифровая часть видеопреобразователя Цифровая часть видеопреобразователя передает компьютеру выделенную из входного видеосигнала информацию по синхронизации. Кроме того, она содер- жит схему хронирования и управления, которая запускает АЦП для приема образца в нужные интервалы времени. Низкоуровневая программа для вво- да изображений, выполняемая компьютером, координирует действие цифровой схемы. Вся цифровая схема работает синхронно с задающим генератором. Частота последнего зависит от максимального числа горизонтальных элементов изобра- жения, которое мы хотим иметь на одной строке развертки. В данном случае
Цифровая часть видеопреобразователя 75 Рис. 3.5. Последовательность сканирования изображения. Примечания 1. Диаграмма предполагает, что неподвижное видеоизображение оцифровывается в изо- бражение с разрешением 320 х 200. В каждом изображении с разрешением 320 х 200 имеется ровно 64000 элементов. 2. Процесс оцифровки изображения совершается не слева направо, а сверху вниз. Таким образом направление оцифровки не совпадает с направлением развертки электронного луча. 3. Номера 1, 2, 3, 4, 5 и т. д. показывают порядок оцифровки элементов изображения. Первый оцифрованный элемент с номером 1 в действительности — элемент 0. критерием служит получение 640 элементов изображения на каждую видео- строку. В гл. 2 мы уже видели, что длительность активной части видеостроки составляет 53 мкс. Это соответствует 80 нс на каждый образец. Частота, которая вычисляется делением единицы на это время, составляет 12,5 МГц. Следователь- но, это будет нашей идеальной частотой генератора или частотой таймера. Такую частоту можно получить с помощью генератора, задающего частоту 25 Мгц. Ин- тегральная схема IC8B делит эту частоту пополам для использования потом во всей цепи. Очевидно, что программа, выполняемая компьютером, не будет успевать ре- агировать на данные, поступающие со скоростью 12,5 МГц. Поэтому видеопре- образователь не стремится брать элементы изображения один за другим вдоль видеостроки. Вместо этого он забирает все образцы в одном столбце, а затем дви- гается к следующему столбцу вправо. В этом случае время между образцами, которые забирает видеопреобразователь, будет составлять 63.5 мкс. В течение этого времени образец может быть взят и передан в компьютер до начала оци- фровки следующего образца. Порядок выборки образцов, используемый видео- преобразователем, показан на рис. 3.5.
76 Глава 3. Аппаратные средства цифрового видеопреобразователя Этот метод имеет как достоинства, так и недостатки. Достоинством является то, что на плате видеопреобразователя не нужна память, поскольку образцы мо- гут отбираться и передаваться в PC в реальном времени. (Тем не менее большой объем памяти в PC необходим.) Недостатком можно считать большое время, не- обходимое для оцифровки изображения. Зная, что для отбора каждого образца требуется 63,5 мкс, можно оценить следующие времена ввода изображений: Разрешение Режим Память, которую необходимо иметь в PC, К байт Время, необходимое для оцифровки, с 320 х 200 Черно-белый 64 4,06 320 х 200 Цветной 192 12,18 640 х 200 Черно-белый 128 8,12 640 х 480 Черно-белый 307 19,51 Из анализа времен оцифровки можно понять, почему могут быть оцифрованы только неподвижные видеоизображения. Если за время оцифровки происходит заметное движение объекта, который оцифровывается, то полученное в резуль- тате изображение будет искажено. Для обеспечения точного времени запуска АЦП используются аппаратные счетчики элементов изображения. Эту функцию выполняет десятиразрядный счетчик, включающий интегральные схемы IC10, 11 и 12, а также защелки IC14 и 15 (отметим, что в защелке IC15 используются только два из восьми разря- дов). Десять разрядов требуются потому, что счетчик должен вмещать число 640 (общее число элементов изображения в строке). Защелки загружаются (с помощью низкоуровневой программы ввода изображений) номером столбца эле- ментов изображения, который будет оцифровываться. Отсчет элементов изобра- жения загружается программой только один раз для каждого видеостолбца. Аппаратура перезагружает себя автоматически (если сигнал GO, поступающий из PC, будет истинным) по мере оцифровки каждого элемента в видеостолбце. Фронт спада синхроимпульса загружает запертое значение отсчета элементов в счетчики элементов изображения. Фронт роста синхроимпульса, совпадающий с фронтом роста буферированных тактовых импульсов, запускает счетчики для обратного счета со скоростью 12.5 МГц. Схема IC13 обеспечивает синхрониза- цию с задающим генератором. Как только счетчик досчитает до нуля, на схему управления АЦП посылается запускающий импульс, который заставляет АЦП сразу же выполнять оцифровку видеосигнала. Отсчет нуля определяется с по- мощью вентилей IC4D и IC9A. Вентиль IC9A проводит логическую операцию И с выходными минимальными и максимальными значениями каждого из трех счетчиков элементов изображения 74LS191. Когда все счетчики содержат нуле- вое значение, уровень сигнала на выходе IC9A повышается. Такой отсчет нуля будет генерировать запуск импульса преобразования к АЦП в том и только в том случае, если счетчики в текущий момент находятся в режиме готовности к счету. Схема IC4D предотвращает ложный запуск АЦП во время загрузки счетчиков элементов изображения, поскольку счетчики в это время не готовы к счету. На- чало импульса преобразования также производит сброс готовности счетчиков
Цифровая часть видеопреобразователя 77 элементов, при этом они удерживаются в пассивном состоянии до тех пор, пока не произойдет их новый запуск. Бели сигнал GO — ложный, или низкого уровня, счетчики элементов изображения не запускаются. В итоге значение, загружаемое в счетчик элементов изображения, определяет задержку от фронта роста синхроимпульса до того элемента, который мы хотим перевести в цифровой код. Так как процесс оцифровки протекает слева направо, загружаемые в счетчик значения возрастают. Когда программа определяет, что все 640 элементов изображения оцифрованы, она узнает об окончании процесса ввода изображения. Загрузка защелок отсчета элементов изображения является многошаговым процессом. Необходимость такой загрузки связана с тем, что принтерный интер- фейс может выводить за один раз только 8 бит, в то время как для счетчиков требуется 10 бит. Для решения этой проблемы аппаратура сконструирована та- ким образом, что каждая половина счетчика загружается отдельно. Первыми загружаются восемь младших битов, а за ними следуют два старших бита. Этот процесс протекает следующим образом: 1. Восемь младших битов значения счетчика помещаются на принтерный порт данных. 2. Выходная линия HIGH/LOW устанавливается на низкий уровень. Это по- казывает аппаратуре, что будет адресоваться первая защелка. 3. Чтобы зафиксировать в аппаратуре значение, переключается линия STROBE. 4. Два старших бита значения счетчика помещаются на принтерный порт данных. 5. Выходная линия HIGH/LOW устанавливается на высокий уровень. Это показывает аппаратуре, что будет адресоваться вторая защелка. 6. Чтобы зафиксировать значение в аппаратуре, снова переключается линия STROBE. После выполнения этой последовательности операций, защелки счетчика эле- ментов изображения будут содержать номер столбца элементов изображения, который следует оцифровать. Схема управления АЦП включает элементы IC5-IC7. Схема имеет два вхо- да — начала преобразования и сброса конца преобразования — а также два вы- хода — синхронизации АЦП и конца преобразования. На фронте роста импульса преобразования (импульс генерируется вентилем IC4D и рассмотрен выше) схе- ма генерирует для АЦП два тактовых импульса с частотой 12,5 МГц. При рабо- те в режиме с бесконечным резервированием, как в данной конструкции, АЦП завершит преобразование за два тактовых периода. Преобразование, для выпол- нения которого требуется приблизительно 120 нс, будет закончено на фронте спада второго импульса. Такая скорость работы АЦП исключает необходимость установки перед ним схемы квантования с запоминанием. Завершение преобра- зования также устанавливает в соответствующее положение защелку IC6A. В компьютер передается сообщение о конце состояния преобразования и програм- ма забирает данные оцифрованного элемента изображения.
78 Глава 3. Аппаратные средства цифрового видеопреобразователя Передача 6 бит (8 бит, если используется АЦП СА3318С) данных элемента изображения в PG усложняется тем, что имеется только четыре входные ли- нии. Чтобы забрать все данные образца, необходимо использовать дополнитель- ную схему мультиплексирования (применив IC3). Для выбора той части данных, которая будет передаваться в PC, снова используются линии видеопреобразова- теля HIGH/LOW. Когда линия HIGH/LOW переходит на низкий уровень, стано- вятся доступными четыре младших бита данных АЦП. Для того чтобы забрать все 6 бит данных элемента изображения, управляющая программа должна: 1. Считать и записать четыре младших бита данных элемента изображения. 2. Установить линию HIGH/LOW на высокий уровень, чтобы иметь возмож- ность забрать два старших бита. 3. Считать два старших бита данных элемента изображения. 4. Объединить данные в одно шестиразрядное значение и записать его. Отметим, что, когда происходит переключение сигнала HIGH/LOW, проис- ходит сброс защелки конца преобразования (IC6A), причем недостаточное чи- сло сигнальных линий управления делает также необходимым мультиплекси- рование этого сигнала. В действительности все хорошо срабатывает, поскольку программа будет знать о завершении преобразования до того, как она попыта- ется забрать данные элемента изображения. Сброс защелки конца преобразова- ния, таким образом, происходит как часть обычного протокола передачи данных без дополнительного программного кода. Обратите внимание также на инвертор IC19G на пути прохождения оцифрованных данных. Он компенсирует инвертор в схеме принтерного порта PC и дает возможность программе обрабатывать все по- лученные данные как имеющие положительную логику, не инвертируя каждый из 6 бит данных. Последняя часть цифровой аппаратуры, которая будет обсуждаться, называ- ется схемой Sync/End of Conversion. Эта схема состоит из вентиля функции «И» с тремя входами IC9B и триггера D-типа IC8A. Триггер IC8A является детек- тором синхроимпульсов, его выход устанавливается на высокий уровень всякий раз, когда во входном видеосигнале определяется отрицательный переход к син- хроуровню. Этот триггер используется, чтобы надежно фиксировать моменты появления коротких выравнивающих импульсов, которые могли бы пройти не замеченными, если бы использовался только опрос программы. Выход триггера остается на низком уровне до тех пор, пока компьютер не активизирует линию Sync Reset. В этом случае выход триггера возвращается на высокий уровень и будет находиться в таком состоянии до прихода следующего синхроимпуль- са. Если линия Sync Reset сохраняется компьютером утвержденной (высокого уровня), вклад детектора синхроимпульсов в функцию Sync/End of Conversion (SyncEOC) становится неразрешенным. Это свойство используется программой ввода изображений. Вентиль IC9B объединяет исходный сигнал синхронизации, выходной сигнал описанной ранее схемы детектора синхроимпульсов, если этот сигнал разблоки- рован, и сигнал End of Conversion из схемы управления АЦП. Выход вентиля устанавливается на низкий уровень, если любой из трех его входов имеет низ- кий уровень. Любой из приведенных ниже сценариев будет вынуждать линию
Сборка видеопреобразователя 79 «SyncEOC» (от видеопреобразователя к PC) переходить на активный (низкий) уровень: а) Генерируется сигнал End of Conversion от схемы управления АЦП. Это означает, что образец подготовлен для передачи в PC. Выход остает- ся на низком уровне до тех пор, пока защелка конца преобразования не будет установлена в исходное состояние путем переключения сигнала HIGH/LOW. б) Если цепь детектора синхроимпульсов заблокирована, выход будет пере- ключаться на активный уровень на то время, пока во входном видеосиг- нале детектируется какой-либо синхроимпульс. в) Если цепь детектора синхроимпульсов разблокирована, выход переключа- ется на низкий уровень на отрицательном фронте любого синхроимпульса и остается на таком уровне до тех пор, пока не будет утверждена линия Sync Reset и не пройдет синхроимпульс. Очевидно, что информация, переданная в PC в виде сигнала SyncEOC, явля- ется контекстно-зависимой. Чтобы правильно интерпретировать эту информа- цию, программа должна точно знать, какая часть видеосигнала обрабатывается. И наконец, все одноразрядные выходные линии от принтерного порта PC к видеопреобразователю согласуются малой резисторно-емкостной схемой. Такая схема необходима, поскольку все компьютеры семейства PC, которые использу- ются при разработке видеопреобразователя, производили кратковременные (ме- нее 50 нс) импульсные помехи при неоднократном обращении к выходным лини- ям. Эти помехи вызывали во время работы разного рода причудливые эффекты. Резисторно-емкостные схемы имеют такую постоянную времени, что кратковре- менные импульсные помехи не могут распространяться в цепи видеопреобразова- теля. Восемь выходных линий данных к видеопреобразователю никогда быстро не изменяются, поэтому в данном случае необходимость подавления помех не возникала. Сборка видеопреобразователя Процесс сборки видеопреобразователя достаточно прост. Из-за относительно низ- ких скоростей передачи в схеме размещение деталей не имеет решающего зна- чения. Видеопреобразователь можно собрать, используя множество различных методов. Например, его можно смонтировать накруткой или спаять на макете, либо использовать печатную плату. На рис. 3.6 представлен фотоснимок прототи- па видеопреобразователя, который смонтирован накруткой. С этим прототипом, с тех пор как он был собран, никогда не возникало никаких проблем, свиде- тельствующих о непригодности конструкции, смонтированной накруткой. Ниже приведены моменты, которые следует иметь в виду при сборке своего видеопре- образователя: 1. Везде, где возможно, делайте короткие соединения. 2. Детали устанавливайте по возможности ближе друг к другу. 3. Провода для аналогового сигнала должны быть короткими. Для подклю- чения к аналоговому входу используйте экранированный провод.
80 Глава 3. Аппаратные средства цифрового видеопреобразователя Рис. 3.6. Макетная плата видеопреобразователя. 4. Попытайтесь все связи заземления провести в одну точку. 5. Для заземления, а также для подключения к источникам питания, ис- пользуйте более толстый провод. 6. Если готовое изделие вызывает помехи в расположенных по близости теле- и радиоприемниках, накройте его металлическим корпусом. 7. Работайте осторожно и не торопитесь. Легче и быстрее сразу смонтировать правильно, чем переделывать все заново. 8. Если вы монтируете свой видеопреобразователь накруткой, используйте пронумерованные бирки, которые прикрепляются снизу к гнездам для ин- тегральных схем и указывают номера контактов деталей. Это поможет значительно сэкономить время, поскольку вам не придется постоянно счи- тать контакты для накрутки на нижней части гнезд для интегральных схем. Эти бирки предотвратят также путаницу с номерами контактов, ко- гда вы будете вращать плату в процессе сборки. 9. Тщательно проверьте вашу работу перед тем, как подключать питание. Для проверки каждой связи используйте омметр. Проверяйте от верхнего конца одних интегральных схем до верхнего конца других и от проводов дискретных компонент до интегральных схем и т. д. При этом проверя- ются не только соединения, но также и гнезда компонент. Особенно по- заботьтесь о том, чтобы между схемой видеопреобразователя и разъемом PC было соединение заземления. Если заземление отсутствует, не будет отнесения сигналов к общей контрольной точке и видеопреобразователь
Тестирование видеопреобразователя 81 никогда не заработает. Кроме того, без общего заземления возможно (хо- тя и маловероятно) повреждение принтерного порта, поэтому будьте осто- рожны. Тестирование видеопреобразователя Не пытайтесь испытать ваш видеопреобразователь или получить изображение до тех пор, пока вы не выполните п. 9 в разделе сборки. Гораздо легче исправить неполадки, связанные с накруткой, чем пытаться найти неисправность в повре- жденном видеопреобразователе. Если в электрической схеме проверены все со- единения, следует, перед тем как пытаться получить изображение, выполнить проверку самой электрической схемы и провести настройку. Для контроля луч- ше использовать осциллограф, хотя это и не обязательно. Чтобы провести пол- ную проверку, необходимо включить питание цепи видеопреобразователя. Перед этим нужно принять следующие меры предосторожности: 1. Не вставляйте в гнезда на видеопреобразователе интегральные схемы (ИС). 2. Не присоединяйте к видеопреобразователю вашу видеокамеру. 3. Не подключайте видеопреобразователь к компьютеру. 4. Не присоединяйте источник питания к цепи видеопреобразователя. Во-первых, подключите источник питания к сети переменного напряжения и проверьте его постоянное выходное напряжение. Это напряжение будет зависеть от используемого источника питания, но в любом случае должно составлять от 8 до 12 В. Если показания не укладываются в этот диапазон, источник питания следует заменить. Соедините выход источника питания с цепью видеопреобразователя. При этом должен загореться светодиодный индикатор (D2). С помощью тестера про- верьте действие регулятора напряжения. На контактах питания каждой ИС должно быть напряжение 5 В с точностью до 10%. Чтобы обеспечить макси- мальную точность тестирования, напряжение питания следует измерять между контактом соответствующие гнезда. Снова включите питание видеопреобразова- теля и проверьте напряжение питания еще раз. После этого проверьте работу задающего генератора. Необходимый для про- верки прибор (осциллограф или тестер) можно подключить непосредственно ме- жду выходным контактом генератора й «подвешенной землей». С помощью ос- циллографа можно исследовать форму тактовых импульсов, чтобы проверить частоту, симметрию (прямоугольной формы импульсы или нет?) и времена на- растания и спада фронтов. Очень большие времена могут затруднить работу видеопреобразователя. Действие задающего генератора можно проверить с по- мощью тестера, если выбрать шкалу переменного напряжения и посмотреть, есть ли вообще сигнал. Если прибор покажет нулевое значение, генератор, вероятно, не работает. Наличие сигнала от генератора следует проверить во всех частях цепи, где этот сигнал необходим. Наиболее вероятной причиной нарушения функционирования задающего ге- нератора является неправильное соединение. Маловероятно, что генератор, из- 6-3
82 Глава 3. Аппаратные средства цифрового видеопреобразователя готовленный фирмой, имеющей высокую репутацию, будет неисправен. Прове- ряйте намотку до тех пор, пока не будет обнаружена и исправлена ошибка. Следующим действием будет калибровка источников напряжения уровня бе- лого и черного. Соедините выводы тестера с входом АЦП, соответствующим уров- ню черного (VREF-, контакт 10), и землей. Вращайте регулятор до тех пор, пока не получите значение 0,34 В. Затем переставьте положительный вывод тестера к входу АЦП, соответствующему уровню белого (VREF-, контакт 9). Вращайте ре- гулятор уровня белого до тех пор, пока не получите значение 1,0 В. После этого еще несколько раз проверьте напряжения на обоих входах АЦП, регулируя их по необходимости так, чтобы привести в соответствие со спецификацией. Между этими двумя регулировками будет существовать некоторая зависимость. Добив- шись необходимой настройки уровней напряжения, регуляторы можно закре- пить, так чтобы на их установку не повлияла вибрация. Соедините видеокабель с видеовходом видеопреобразователя Л. С помощью тестера убедитесь (на установочном конце видеокабеля), что экранирующая часть видеоразъема соединена с «подвешенной землей» видео- преобразователя, что внутренний подающий контакт видеоразъема не заземлен и что на этом контакте нет напряжения. Эта проверка используется, чтобы не допустить повреждения видеокамеры. Если проверка прошла успешно, соеди- ните ваш источник видеосигналов с входным разъемом видеопреобразователя Л. С помощью осциллографа или тестера удостовертесь, что сигнал на выходе компаратора IC1 постоянно изменяется. С помощью осциллографа вы можете убедиться, что изменения совпадают с синхроимпульсами во входном видеосиг- нале. На этом вся работа по проверке схемы видеопреобразователя, которая мо- жет быть выполнена без поддержки программного обеспечения PC, закончена. Видеопреобразователь можно безопасно присоединить к PC при условии, что электропитание обоих приборов в это время будет отключено. Если проверка ви- деопреобразователя до сих пор проходила успешно, тогда вы можете попытаться оцифровать изображение. Если вы в этом преуспели — поздравляю! Вы сделали свой цифровой видеопреобразователь. А если нет, не расстраивайтесь. Мы ис- пользуем программу, которая описывается в следующей главе, для дальнейшего поиска неисправностей в аппаратуре видеопреобразователя. Найти и устранить неисправность будет относительно легко. Заключение В этой главе мы обсуждали общий проект, конструкцию, принцип действия и проверку аппаратуры нашего видеопреобразователя. Обсуждение программного обеспечения в следующей главе покажет, как, казалось бы, не имеющие отно- шения друг к другу части аппаратуры взаимодействуют и позволяют в конеч- ном итоге получать переведенные в цифровой код видеоизображения высокого качества.
Глава 4 Программные средства видеопреобразователя низкого уровня для ввода изображения В главе рассмотрены следующие вопросы: • Влияние тактовой частоты процессора на работу видеопреобразователя. • Использование структуры данных Image Request для управления работой видеопреобразователя. • Работа таймеров аппаратных средств в PC. • Функции поддержки видеопреобразователя низкого уровня. • Ввод изображения видеопреобразователем. • Завершение тестирования вашего видеопреобразователя. Введение В главе представлены все программные средства низкого уровня, необходимые для получения видеоизображений в цифровом виде. Полный набор программ- ных средств видеопреобразователя представлен здесь и в следующих главах: от процедур манипуляции битами наинизшего уровня в коде ассемблера до в не- которой степени запутанного алгоритма срединного отсечения для получения цветных изображений. Эта информация поможет вам узнать, как работает ви- деопреобразователь и как модифицировать его работу для ваших нужд, как ра- ботает графический адаптер VGA и как он программируется. Влияние быстродействия центрального процессора В целях снижения стоимости этого проекта было принято решение использовать PC для восприятия и анализа видеосигнала, который необходимо представить в цифровом виде, так чтобы не встраивать в видеопреобразователь дополнитель- ных аппаратных средств. Этот подход действительно позволил не повышать стои- мость аппаратных средств, но он предъявил большие требования к программным средствам. Возник даже вопрос, сможет ли PC поддерживать видеосигнал, ко- торый он пытается воспринять. Вопрос свелся к тому, имеет ли PC достаточное количество циклов процессора для выполнения команд между событиями в ви- деосигнале. Если видеосигнал может меняться в процессе выполнения одной ко- манды центрального процессора или выполнения компактного цикла, PC будет терять видеоинформацию и процесс преобразования в цифровую форму будет на- рушен. Однако, если существует достаточное количество циклов центрального 6*
84 Глава 4. Программные средства цифрового видеопреобразователя процессора, вся видеоинформация может быть воспринята и будет получено вы- сококачественное изображение в цифровом виде. Требования, предъявляемые к PC процессом преобразования в цифровую форму, определяют принадлежность программных средств к категории реального времени. Программные средства реального времени по природе должны быть детерминистическими. Это означа- ет, что они должны действовать в пределах предсказуемого, обычно короткого промежутка времени. Очевидно, что требования реального времени для исполнения кода легче вы- полнить с более мощными системами обработки или центральными процессора- ми. Это объясняется двумя причинами: не только большее количество циклов становится доступным благодаря более высоким тактовым частотам, но более новое поколение процессоров выполняет большинство команд за меньшее чи- сло циклов. Таким образом, временные характеристики программных средств становятся менее решающими для работы на более современных и быстродей- ствующих PC. Однако для более быстродействующих PC возникают собственные проблемы, которые мы вкратце изложем ниже. Другая цель создания видеопреобразователя — сделать его работающим с любым IBM-совместимым PC независимо от быстродействия. Это создает опре- деленные проблемы в совместимости программных средств, что также является предметом рассмотрения данного раздела. Эта цель достигнута, поскольку ви- деопреобразователь был успешно использован с различными PC от 4,77 МГц IBM PC до 20 МГц PS/2 модели 70. Однако для машин с различным быстродействием необходимы некоторые различия в программе драйвера видеопреобразователя низкого уровня. Прежде чем рассматривать модификацию программных средств для PC с различным быстродействием, необходимо поговорить о том, какая информация, содержащаяся в видеосигнале, действительно воспринимается, и какие функции поддержки видеопреобразователя должны быть выполнены. Ниже, при обсуждении процедуры GetPicture, вы увидите, как выполняется реальная работа. Функции драйвера низкого уровня, которые составляют обра- ботку данных в реальном времени, могут быть классифицированы следующим образом: а) Детектирование и счет синхронизирующих импульсов. б) Пересылка данных о цифровых элементах изображения. в) Обслуживание счетчика элементов изображения. г) Сброс фиксатора состояния. д) Поддержка таймера PC. Каждая из этих функций влияет на обработку данных в реальном време- ни. Три категории временных критериев вступают в действие при модификации драйвера видеопреобразователя низкого уровня для использования в PC с кон- кретной тактовой частотой: а) Время доступа к порту ввода-вывода. б) Длительность программного стробирующего импульса. в) Временные соотношения детектирования синхроимпульса.
Влияние быстродействия центрального процессора 85 Ниже описаны основные пути модификации кода драйвера низкого уровня для определенных классов PC. Время доступа к порту ввода-вывода имеет отношение к необходимому време- ни восстановления между последовательными доступами к устройствам ввода- вывода, соединенным с шиной PC. Если возникают попытки последовательных записей в порт принтера без требуемого восстановления, порт принтера будет создавать серьезные помехи и данные могут оказаться не теми, которых можно было бы ожидать. Макрокоманда SDelay, содержащаяся в коде драйвера низ- кого уровня, модифицируется для уточнения того, сколько времени необходимо на восстановление между операциями ввода-вывода. Эта небольшая задержка необходима, потому что для некоторых системных плат и канальных операций не хватает времени восстановления между последовательными доступами. Эта малая задержка обеспечивается командой перехода на следующую команду в последовательности кода, что записывается следующим образом: jump short $+2 Проблема времени доступа к порту ввода-вывода впервые возникла для ма- шин класса PC АТ и с тех пор характерна для новых изделий семейства PC. Из опыта, полученного при развитии программных средств видеопреобразова- теля низкого уровня, макрокоманда SDelay в зависимости от тактовой частоты центрального процессора имеет следующее содержание: Класс компьютера Тактовая частота, Мгц Число команд перехода РС 4,77 (8088) 0 PC АТ 6 или 8 (80286) 1 PC АТ 10-16 (80286) 1 или 2 более поздние 16 и больше 2 Число команд перехода, которые необходимо включить в макрокоманду SDelay, найдено экспериментальным путем. Для вашего конкретного компью- тера может потребоваться дальнейшее экспериментирование. Если созданное видеопреобразователем изображение имеет полосы, нужно увеличить число переходов в SDelay (максимально до 2) и посмотреть, будет ли решена проблема. Для проверки наличия помех на какой-либо из линий упра- вления между PC и видеопреобразователем можно использовать осциллограф. Некоторые из этих помех имеют длительность всего лишь 50 нс, таким обра- зом, чтобы их увидеть, требуется масштаб малых времен. Кстати, эти команды перехода также необходимы между доступом к какому-либо устройству ввода- вывода и инструкцией STI. В техническом справочном руководстве IBM говорит- ся, что правильный способ выполнения этой операции состоит в следующем: out I/0_Addr, al jmp short $+2 mov al,ah sti (В 4,77-МГц IBM PC (или совместимом) макрокоманда SDelay может полностью отсутствовать, поскольку процессор 8088 недостаточно быстродействующий для
86 Глава 4. Программные средства цифрового видеопреобразователя того, чтобы возникала проблема с последовательными доступами к порту вводаг вывода.) Длительность программного стробирующего импульса определяет, как долго линии управления выводом от PC к видеопреобразователю находятся в устано- вленном состоянии перед тем как возвратиться в состояние покоя. Поскольку определение времени стробирования осуществляется программно, длительность импульса стробирования будет зависеть от быстродействия PC. Макрокоманда StbDel управляет длительностью программных стробов и поэтому также должна быть модифицирована в соответствии с быстродействием компьютера. Для пра- вильной работы схемы видеопреобразователя необходима длительность строба от 2 до 5 мкс. Для более длинных кабельных соединений между PC и видео- преобразователем требуется строб большей длительности. Типичная последова- тельность команд для генерирования сигнала стробирования следующая: mov al, ResetSyncAndEOC out dx, al StbDel mov al,PrtPortContSafe out dx, al Первая команда out устанавливает выходной сигнал, тогда как вторая сбрасы- вает его. Длительность выходного сигнала стробирования управляется макро- командой StbDel. Расчет времени, требуемого для выполнения ряда команд, необходим для определения длительности импульса стробирования. Вернее сказать, когда из- вестна требуемая длительность строба, команды располагаются вместе (и поме- щаются в макрокоманду StbDel), что требует времени для их выполнения. Для расчета этого времени необходима следующая информация. Во-первых, время цикла процессора, которое определяется тем, насколько быстрым является сиг- нал синхронизации, и, во-вторых, число циклов процессора, требуемое для вы- полнения определенных команд, сообщаемое изготовителем процессора. В табл. 4.1 приведено типичное число циклов процессора для различных команд, ко- торые выполняются на процессорах семейства Intel, используемых в IBM PC и совместимых компьютерах. Если принять, что доступы к памяти имеют нулевой режим ожидания и во время выполнения команд не возникает исключительных ситуаций, общее время для серии команд рассчитывается путем умножения суммарного числа процес- сорных циклов на период генератора тактовых импульсов процессора. Этот пе- риод для процессора 8088 с частотой 4,77 МГц составляет 210 нс, а для 16-МГц 80386 — 62,5 нс. Эти приблизительные числа необходимо немного изменить, при- нимая во внимание обновление памяти, которое может препятствовать доступу процессора к памяти каждый раз, когда это требуется. Число 7% взято из техни- ческого справочного руководства IBM. Скорректированные периоды генератора, таким образом, равны 225 и 66,9 нс соответственно. Эти времена должны исполь- зоваться во всех расчетах длительности команд. Итак, чтобы сконструировать макрокоманду StbDel для определенной такто- вой частоты процессора, подберите ряд команд (обычно NOP или Push и Pop), которые вместе требуют для выполнения от 2 до 5 мкс. Длительность генери- руемого импульса стробирования может быть проверена путем подсоединения осциллографа к выводу сигнала стробирования от PC к видеопреобразователю.
Влияние быстродействия центрального процессора, 87 Таблица 4.1. Временная диаграмма команд процессора Intel Команда 8088/8086 80286 80386 and reg,reg 3 2 2 and reg,immed 4 3 2 call NEAR 19 8 8 call FAR 28 17 22 di 2 3 3 in acc,immed8 10 5 12 in acc,dx 8 5 13 je/jz 16/4*1 8/3*1 8/3*1 jne/jnz 16/4*1 8/3*1 8/3*1 jmp 15 8 8 nop 3 3 3 or reg,reg 3 2 2 or reg,immed 4 3 2 out immed8,acc 10 3 10 out dx,acc 8 3 11 POP ax 8 5 5 push ax 11 3 5 ret NEAR 16 11 10 ret FAR 26 15 18 sti 2 2 2 Примечания а) (*1) означает, что, когда происходит переход, требуется больше циклов, чем когда переход не происходит. б) Все числа циклов процессора взяты из справочных данных по процессорам Intel и предпо- лагают режим реальной адресации. в) Полное время для последовательности команд рассчитано путем умножения суммарного числа процессорных циклов, необходимого для выполнения команд из этой таблиц ы, на такто- вую частоту процессора. Рассчитанные времена обычно будут отличаться от реальных времен выполнения не более чем на 5-10%. Последний вопрос, который необходимо затронуть в этом разделе, касает- ся времени детектирования синхроимпульса. Тонная синхронизация видеоим- пульсов необходима, чтобы позволить программе синхронизироваться с видео- сигналом. Синхронизация позволяет PC определить, где кончается управляю- щая часть видеосигнала и начинается видимая часть. Синхронизация достигает- ся путем помещения в видеосигнал кадрового синхроимпульса. Когда кадровый импульс локализован, PC может начинать подсчет выравнивающих и строчных синхроимпульсов, чтобы определить, где начинается видимая часть видеосигна- ла. Как только синхронизация будет достигнута, начинают работать аппарат- ные средства видеопреобразователя, и видеосигнал преобразуется в цифровую форму. Обнаружение кадрового синхроимпульса основывается на его длительности. Кадровые синхроимпульсы (также называемые кадровыми синхронизирующими интервалами) можно отличить от строчных и выравнивающих синхроимпульсов по их длительности, составляющей 27,1 мкс. Любой синхроимпульс с продолжи- тельностью более 15 мкс может рассматриваться как кадровый синхроимпульс.
88 Глава 4. Программные средства цифрового видеопреобразователя Строчные синхроимпульсы имеют длительность примерно 4,7 мкс, тогда как вы- равнивающие импульсы — 2,3 мкс. Поэтому есть гарантия, что любой синхроим- пульс длительностью более 15 мкс является кадровым. Основной алгоритм мето- дики, используемой в коде программы поддержки видеопреобразователя низкого уровня для обнаружения кадрового синхроимпульса, следующий: Цикл Считывание линии SyncEOC из преобразователя Пока Sync не активен (low ж active) Считываем линию SyncEOC из преобразователя (Сигнал Sync стал активных) Задержка (до 15 миллисекунд) Повторное считывание линии SyncEOC из преобразователя Если Sync до сих пор активен Выход из цикла. Определен вертикальный Sync Конец Цикла Видно, что правильная работа алгоритма зависит от того, доступна ли срав- нительно точная (и устойчивая) задержка. Существуют два метода обеспечения этой задержки: набор команд, требующий для выполнения 15 мкс или использо- вание аппаратного таймера PC. В программе поддержки видеопреобразователя определена макрокоманда LDELAY для выбора метода задержки, используе- мого для детектирования синхроимпульса. Пока отметим, что подход с исполь- зованием аппаратного таймера — лучшее решение, но его можно осуществить только на сравнительно быстродействующих PC (PC АТ и выше). Этот метод используется в драйвере низкого уровня, представленного ниже. Более полная информация по использованию таймеров PC для синхронизации детектирования синхроимпульса приведена в разделе, посвященном методу Delay. Для более медленных PC выбирается метод, по которому выполняется спи- сок команд, поскольку процессоры не имеют дополнительных циклов для вза- имодействия с аппаратным таймером. Для создания подходящей временной за- держки можно использовать команды NOP (каждая требует трех циклов). Ко- гда подходящее число команд определено (путем проведения расчетов времени выполнения, как описано выше), команды могут быть введены в макрокоманду LDELAY, и программа низкого уровня заново оттранслирована. Для полной точ- ности времена выполнения других команд, окружающих макрокоманду, должны быть включены в расчеты времени выполнения. В заключение отметим, что при конечной настройке программы драйвера низ- кого уровня необходимо принять во внимание быстродействие PC, совместно с которым используется видеопреобразователь. В зависимости от быстродействия процессора необходимо изменить три различные макрокоманды. Имеется руко- водство для правильного изменения этих макрокоманд. Структура данных ImageReq Структура данных ImageReq, представленная ниже, является механизмом, с по- мощью которого программа высокого уровня на языке Си управляет работой аппаратных средств видеопреобразователя. Она формирует интерфейс между кодами высокого и низкого уровней. Манипулирование этой структурой данных
Структура, данных ImageReq 89 позволяет выполнить значительные изменения в конечном изображении в ци- фровом виде. Все элементы поддаются конфигурированию в рамках структуры ImageReq. Результат изменения этих элементов мы вкратце здесь обсудим. /* Структура ImageReq» передаваемая между Си и ассемблером для управления преобразователем ♦/ struct ImageReq enum Computer ComputerType; /* не используется ♦/ unsigned PrtBase; /♦ базовый адрес порта принтера ♦/ enum HorizMode HMode; /♦ 320/640 точек по горизонтали ♦/ enum VertMode VMode; /♦ 200/480 точек по вертикали ♦/ unsigned NumberOfPasses; /♦ не используется ♦/ unsigned long Flags; /♦ не используется ♦/ char huge «PictBuf; /♦ пара сегмент/смещение буфера изображения ♦/ unsigned FirstLine; /♦ первая линия оцифровываемого изображения ♦/ unsigned FirstPixel; /♦ первая точка первой линии ♦/ unsigned LastLine; /♦ последняя линия оцифровываемого изображения ♦/ unsigned LastPixel; /* последняя точка последней линии ♦/ Отметим, что некоторые из элементов этой структуры данных обозначены как «в настоящее время не используется». Они введены для усовершенствова- ния в будущем программных средств низкого уровня, преобразующих изображе- ние в цифровой вид, и не будут обсуждаться в дальнейшем. Определение этой структуры данных находится в файле digitize.h, представленном в листинге 4.1. Листинг 4.1. Код драйвера видеопреобразователя низкого уровня Ниже приводится содержимое файла digitize.h: /дедедедедедедедедедедедедедедедедедедеде/ /♦ Header-файл ♦/ /♦ драйвера видеопреобразователя ♦/ /♦ для использования в программах на Си ♦/ /* разработан в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 12/07/89 ♦/ /деде* ♦♦♦ ****** ** ************************/ #ifndef BYTE /♦ тип определяемый пользователем ♦/ «define ..BYTE typedef unsigned char BYTE; «endif «define TRUE 1 «define FALSE 0 «define VIDEO 0x10 /♦ номер видеопрерывания BIOS ♦/ «define LPT1 ОхЗВС /♦ базовый адрес LPT1 ♦/
90 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4.1. (Продолжение) typedef enum {BV,Color} Cmode; typedef enum {CGAL,CGAH»EGAL»EGAH,VGAL»VGAM,VGAH} Display; typedef enum {PC477»AT6»AT8»PS210»PS216»PS220»PS225,PS233} Computer; typedef enum {LovRes,HighRes} HorizMode; typedef enum {NonInterlace»Interlace} VertMode; /♦ Структура данных ImageReq» передаваемая между Си и Ассемблером для управления видеопреобразователем */ struct ImageReq { enum Computer unsigned enum HorizMode enum VertMode unsigned unsigned long char huge unsigned unsigned unsigned unsigned }; ComputerType; PrtBase; HMode; VMode; NumberOfPasses; Flags; ♦PictBuf; FirstLine; FirstPixel; LastLine; LastPixel; /♦ прототипы функций ♦/ unsigned InitializeDigitizer (struct ImageReq ♦); void SetPixelCount (unsigned short); unsigned short SyncsPerField (void); unsigned GetPicture (void); Далее следует содержимое файла digitize.asm: ; Общие подпрограммы драйвера видеопреобразователя ; Эта версия использует таймер/счетчик 8253 для подсчета синхроимпульсов ; и» следовательно» аппаратно независима. > ; написана Крейгом А. Линдли ; Последние изменения внесены: 05/17/89 Примечания: большинство кодов в этом файле написано в форме inline. ; Любые изменения в этой структуре могут привести ; к остановке видеопреобразователя. Любое изменение в ; используемых регистрах также может вызвать прекращение работы. ; Очень осторожно изменяйте параметры» участвующие в компоновке ; с ходом на Си. .TEXT segment byte public ’CODE’ DGROUP group .DATA».BSS assume cs:.TEXT»ds:DGROUP»ss:DGROUP .TEXT ends
Структура, данных ImageReq 91 .DATA segment word public ’DATA’ FieldlFound DW False ;True после локализации полукадра 1 Fieldldone DW False ;True после оцифровки полукадра 1 PrtPortData DW 0 ;порт вывода,данных PrtPortln DW 0 ;порт ввода данных (5 бит) PrtPortCont DW 0 ;порт контроля выходных линий ImageReqPtr DW 0 ; указатель на структуру ImageReq DigitInit DW False ;True когда видеопреобразователь • инициализиров ан CompType DW 0 ; содержит тип конфигурации PC Эти переменные используются только» когда изображение оцифровывается в чересстрочном режиме. Они содержат адрес, по которому будет храниться следующая оцифрованная колонка. Это необходимо из-за того, что данные в режиме Interlaced для единичного кадра должны храниться последовательно в буфере изображения. PColSeg PColOff DW DW 0 0 ; сегмент для хранения текущей видеоколонки ; смещение в PColSeg для текущей видеоколонки Idata .BSS ends segment word public 1 ’BSS’ .BSS ;Общие < True False SegOff ends определения EQU EQU EQU OFFH ;все единицы имеют значение TRUE 0 ;все нули имеют значение FALSE (OFFFFH/1OH)+1 ;параграфы Определение бита PrtPortCont Strobe EQU 1 ;используется для прерывания счета SyncReset EQU 2 ; сброс защелки синхронизации Go EQU 4 ; запуск счетчика элементов изображения HighLow EQU 8 ; выбор линии счетчика элементов ;и линии данных PrtPortContSafe EQU ;Битовые комбинации OBH ; будучи записанным в PrtPortCont ;обнуляет все 4 бит. Бит GO • обрабатывается особым образом SetGo EQU PrtPortContSafe OR GO SetStrobe EQU PrtPortContSafe AND NOT Strobe SetHighLow EQU PrtPortContSafe AND NOT HighLow SetHighLowAndGo EQU PrtPortContSafe AND NOT HighLow OR GO SetSyncReset EQU PrtPortContSafe AND NOT SyncReset SetHighLowAndStrobe EQU PrtPortContSafe AND NOT HighLow AND NOT Strobe SetSyncResetAndGo EQU PrtPortContSafe AND NOT SyncReset OR GO ResetSyncAndEOC EQU PrtPortContSafe AND NOT SyncReset AND NOT HighLow
92 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4.1. (Продолжение) ^Определение бита PrtPortln SyncEOC EQU 8 ;бит используется для определения ; синхроимпульса и конца преобразования (ЕОС) Специальные определения HBlankingOffset EQU 47 ;дополнительный счетчик. Его значение ♦ VBlankingOffset EQU 16 ;составляет 3,81 мкс от возрастающей ;кромки синхроимпульса с частотой 12,38 МГц ; синхроимпульсы, поступающие перед SyncsFieldl EQU 272 ; получением полукадра видеоизображения ; число синхроимпульсов в полукадре 1 SyncsField2 EQU 271 ; число синхроимпульсов в полукадре 2 Структура данных ImageReq Эта структура данных передается в ассемблерный код из языка Си, чтобы сообщить видеопреобразователю о том, что ему следует делать. Данное определение не выделяет памяти, оно только определяет смещение различных элементов структуры данных. Для нормальной работы видеопреобразователя эта структура долина полностью соответствовать структуре данных ImageReq из файла gvideo.h. > ImageReq struc ComputerType DW 0 ; используется для определения времени задержки PrtBase DW 0 ;порт принтера, к которому подключен ;видеопреобразователь HMode DW 0 ; указывает на число точек в строке (320 или 640) VMode DW 0 чересстрочный или нечересстрочный режим. ;200 или 480 строк в видеокадре. NumberOfPasses DW 0 ; число проходов сканирования Flags DD 0 ; набор флагов PictBufOff DW 0 ;пара сегмент/смещение для буфера изображения PictBufSeg DW 0 FirstLine DW 0 FirstPixel DW 0 LastLine DW 0 LastPixel DW 0 ImageReq ends Далее идут определения различных типов PC. Макрокоманды задержки должны быть скорректированы со скоростью работы PC. Приведенные ниже значения представляют собой все возможные значения элемента ComputerType структуры данных ImageReq. РС477 EQU 0 РСАТ6 EQU 1 РСАТ8 EQU 2
Структура, данных ImageReq 93 PS210 EQU 3 PS216 EQU 4 PS220 EQU 5 PS225 EQU 6 PS233 EQU 7 LowRes EQU 0 ;320 точек HighRes EQU 1 ;640 точек NonInterlace EQU 0 ; получение одного полукадра видеоизображения Interlace EQU 1 ; получение целого кадра ; Определения таймера 8253 ;Для хронирования синхроимпульсов используется Инет 2. Такой метод ;является более точным, чем использование программных циклов. Все PC ;имеют данную микросхему. Обычно она используется для вывода сигналов на встроенный громкоговоритель компьютера. ;Ниже приведены адреса таймера и портов ввода-вывода Timer2CountReg EQU 42H ;для установки значения счетчика TimerCmdReg EQU 43H ;для передачи команд микросхеме таймера Timer2GateReg EQU 61H ; адрес регистра вентиля 8255 TermBitReg > EQU 62H ; адрес регистра прерывающего бита 8255 ;Ниже приведены определения масок и байты режимов TimerCount EQU 18 ; период 840 нс Timer2GateBit EQU 01H ;бит 0 порта 8255 TermCntBitMask EQU 20H ; прерывающий бит - бит 5 порта 8255 Timer2Mode EQU 0B2H ; выбран таймер 2 ; режим счета = 1; двоичный счет .TEXT segment byte public ’CODE’ ; Макрокоманды Delay ;Короткая задержка. Она используется для восстановления шины компьютера ;между последовательными событиями доступа к порту ввода-вывода. SDelay MACRO jmp short $+2 jmp short $+2 ENDM ;Задержка стробирования. Используется для того, чтобы программа ; генерировала стробы достаточной длины StbDel MACRO push ax POP ax push ax pop ax
94 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4.1. (Продолжение) push ах pop ах push ах pop ах ENDM ; Начало процедур на языке ассемблера ; Процедура InitializeTiMer2 ;Эта процедура устанавливает таймер 8253 для использования в качестве син- хронизирующего таймера. Используется режим счета 1. В этом режиме, когда ;запирающий сигнал переходит с низкого уровня на высокий, в счетчик загру- жается предварительно заданное значение и таймер начинает счет к нулю. После ;достижения нуля прерывающий бит переходит на высокий уровень. Эта процедура ;устанавливает таймер 2 и счетчик для использования процедурой Delay. ;INPUT: отсутствует. ;OUTPUT: только таймер 2 инициализирован и счетчик загружен. ;USES: регистр АХ. ;CALL: не вызывается из Си. InitializeTiaer2 proc near mov al,TiMer2Mode ;Загрузить номер режима out Ti*erCMdReg,al ;послать его в 8253 SDelay mov ax, Tiner Count out TiMer2CountReg, al SDelay mov al,ah out TiMer2CountReg, al Определить показания счетчика ; установить младший значащий бит определить старший значащий бит установить старший значащий бит ret InitializeTiMer2 endp ; Процедура Delay ;Эта процедура использует таймер 2 для установки задержки длительностью ; приблизительно 15 икс ;INPUT: отсутствует. ;OUTPUT: только таймер 2 используется для создания задержки. •USES: регистр АХ. ;CALL: не вызывается из Си.
Структура данных ImageReq 95 Delay proc near > push in dx al,Timer2GateReg ; сохранить значение регистра ; считать порт 8255 SDelay and out al,NOT Tiner2GateBit Tinter 2Gat eReg, al ; записать 0 в бит 0 ; установить запирающий бит на низкий ;уровень SDelay or out al,Timer2GateBit Timer2GateReg,al ; записать 1 в бит 0 ; установить запирающий бит на высокий ;уровень SDelay Dell: in and jnz al,TerMBitReg al, TermCntBitMask Del2 ;считать прерывающий бит порта {закончен ли период таймера {если да, то перейти SDelay jap short Dell Del2: SDelay pop ret dx {восстановить значение регистра Delay endp ; Процедура InitializeDigitizer {Эта процедура проводит инициализацию видеопреобразователя. Для ;инициализации необходимо установить биты заделки PrtPortCont. Линия ; HighLow должна быть стробирована для очистки линии SyncEOC (поскольку ; заделка ЕОС затек сбрасывается). Если этого не сделать, возможна ;ситуация, при которой линия SyncEOC будет оставаться на низком уровне, ;препятствуя прохождению сигнала синхронизации от видеопреобразователя. ;INPUT: указатель типа near на структуру данных IntageReq. ;OUTPUT: отсутствует. {USES: регистр! АХ, ВХ, DX. ;CALL: вызывается из Си. ;PROTOTYPE: InitializeDigitizer(struct laageReq ♦) . Public -InitializeDigitizer -InitializeDigitizer proc near
96 Глава 4. Программные средства, цифрового видеопреобразователя Листинг 4.1. (Продолжение) push bp ; подготовка к получению указателя BOV bp.sp ;на структуру данных ImageReq ; получение и запоминание указателя MOV bx,[bp+4] ; регистр ВХ указывает на ImageReq mov ImageReqPtr,bx ; сохраняем в виде локальной переменной ;Прежде всего надо получить адрес принтерного порта. Этот адрес необходим ;для обеспечения связи с видеопреобразователем. Все адреса порта ; рассчитываются с помощью основного г адреса - PrtBase. mov ax,[bx].PrtBase ; определение номера порта mov PrtPortData,ax inc ax ;port+l я PrtPortln BOV PrtPortIn,ax inc ax ;port+2 я PrtPortCont BOV PrtPortCont,ax BOV ax,[bx].ComputerType ; считываем тип компьютера BOV CompType ; записываем его в DS ; Теперь можно начинать инициализацию видеопреобразователя BOV dx,PrtPortCont ; указывает на биты управляющей защелки BOV al,PrtPortContSafe ; инициализация управляющих битов out dx,al ; посылаем на видеопреобразователь call Delay ; задержка для стабилизации BOV al,ResetSyncAndEOC ; установка бита HighLow в High out dx,al ;для очистки линии ЕОС StbDel BOV al,PrtPortContsafe ; инициализация управляющих битов out dx,al ; посылаем на видеопреобразователь SDelay BOV ax,0 устанавливаем счетчик точек на 0 call SetPixelCount ; вызываем аппаратные счетчики SDelay call InitializeTimer2 ; инициализируем таймер BOV DigitInit,True ;указывает на то, что видеопре- ; образователь проинициализирован pop bp ; восстанавливаем значение ВР ret ; возврат в код языка Си .InitializeDigitizer endp
Структура, данных ImageReq 97 ; Процедура .SyncPerField ;Эта процедура подсчитывает число синхроимпульсов всех типов, ;которые возникают в одной полукадре видеоизображения. Подсчет ;импульсов проводится за 1/30 с. Полукадр 1 должен содержать 272 ;синхроимпульса, а полукадр 2-271 синхроимпульс. ;INPUT: отсутствует. ;OUTPUT: число синхроимпульсов в полукадре в регистре АХ. ;USES: регистры АХ, СХ, DX. Регистр ВХ восстанавливается. ;CALL: вызывается из Си. ;PROTOTYPE: unsigned short SyncPerField(void) . Public .SyncsPerField .SyncsPerField proc near cli call SyncsPerfield sti ret .SyncsPerField endp ; Процедура SyncsPerField ;Описание см. выше ; отключение прерываний ;счет импульсов ;включение прерываний ;INPUT: отсутствует. ;OUTPUT: число синхроимпульсов в полукадре в регистре АХ. ;USES: регистры АХ, СХ, DX. Регистр ВХ восстанавливается. ;CALL: не вызывается из Си. SyncPerField proc near push bx сохранение регистра BOV bx,7 ; инициализация счетчика для учета ;всех вертикальных синхроимпульсов mov dx,PrtPortCont ; указывает на управляющий регистр mov al, ResetSyncAndEOC ; сброс сигнала ЕОС out dx,al ; только действительные синхроимпульсы ; будут восприниматься аппаратурой SDelay Считывают сигнал SyncEOC, чтобы убедиться в его низком уровне dec dx spf 1: in al,dx and al,SyncEOC jz spf 1 ; указывает на PrtPortCont ; считывание данных ;переход, если сигнал SyncEOC ; имеет низкий уровень 7-3
98 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4.1. (Продолжение) ;Повторная проверка. call Delay in al,dx and al»SyncEOC jz spf 1 ; переход по длинному синхроимпульсу SDelay ;Если мы находимся в этом месте» следовательно был обнаружен короткий ; синхроинтервал. Теперь следует обнаружить первый длинный синхроимпульс ;для запуска счетчика синхроимпульсов. call FindLongSync ;Длинный импульс найден. spf 2: in al»dx and al» SyncEOC jz spf2 ; переход» если сигнал SyncEOC имеет ; низкий уровень ;Повторная проверка. call Delay in al»dx and al» SyncEOC jz spf2 ; переход по длинному синхроимпульсу SDelay ;Повторно обнаружен короткий импульс. С этого момента проводим счет ;импульсов до появления длинного синхроимпульса» который будет указывать ;на конец полукадра. spf3: in al»dx ; считываем входной порт and al»SyncEOC ;маскируем все биты» кроме синхронизирующего jnz spf 2 ;цикл заканчивается при переходе ; синхронизирующего бита на низкий уровень SDelay ;Защелка синхроимпульсов перешла на низкий уровень. Сбрасываем ее. inc dx ; указывает на PrtPortCont MOY al»ResetSyncAndEOC ; устанавливаем сигнал SyncReset out dx»al ;на высокий уровень StbDel MOY al»PrtPortContSafe ; конец сигнала SyncReset out dx»al ; вывод сигнала
Структура, данных ImageReq 99 ;Повторяем операцию считывания. > call Delay dec dx ; указывает на PrtPortln in al,dx and al,SyncEOC jz spf 4 ; переход по длинному синхроимпульсу inc bx ; подсчет коротких импульсов j«P spf3 ;проверка на длинный импульс > spf 4: inc dx ; указывает на управляющий порт BOV al,ResetSyncAndEOC устанавливаем сигнал синхронизации ;и сброса ЕОС на высокий уровень out dx,al SDelay dec dx ; указывает на порт входа 5 BOV ах,Ъх ;помещаем конечное значение в регистр АХ pop Ъх ret SyncsPerField endp ; Процедура FindLongSync ;Эта процедура вызывается во время приема активной видеостроки (т.е. ; после обнаружения коротких синхроимпульсов). В данной процедуре мы ; устанавливаем сигнал сброса синхронизации и сигнал сброса ЕОС (линию ;HighLow). Нас интересует только длинные (вертикальные) синхроимпульсы, ; поэтому мы не используем защелку IC8A. Установка описанных выше линий ;гарантирует появление на линии SyncEOC только нужных синхроимпульсов. ;Поэтому у нас нет необходимости сбрасывать защелку за пределами цикла. ;IHPUT: отсутствует. ;OUTPUT: отсутствует. ;USES: регистры АХ, DX. ;CALL: не вызывается из Си. > FindLongSync proc near BOV dx,PrtPortCont ; указывает на управляющий порт BOV al,ResetSyncAndEOC установка линии синхронизации и сигнала out dx,al ;сброса ЕОС SDelay dec dx ; указывает на порт ввода flsl: in al,dx учитываем порт ввода and al,SyncEOC ;маскируем все биты, кроме синхронизирующего 7*
100 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4.1. (Продолжение) jnz flsl ;продолжаем цикл, пока бит имеет значение О ;Защелка синхронизации установлена на низкий уровень. call Delay ;задержка порядка 15 мкс in al,dx ;считывание сигнала SyncEOC and al,SyncEOC jnz flsl ; продолжаем цикл ;Если мы находимся в этом месте, значит обнаружен длинный синхроимпульс. SDelay ret FindLongSync endp ;Процедура SetPixelCount ;Эта процедура устанавливает защелку счетчика элементов изображения на ; плате видеопреобразователя на 16-разрядное значение из регистра IX. В ; настоящее время видеопреобразователь использует только 12 из 16 разрядов ;этого счетчика. ;INPUT: число элементов в регистре АХ. ;OUTPUT: отсутствует. ;USES: регистры АХ, СХ, DX. ;CALL: не вызывается из Си. SetPixelCount proc near mov ex, ax ;записываем число элементов в регистр СХ mov dx,PrtPortData ; указывает на порт вывода данных mov al, cl ; получаем младший значащий бит числа ; элементов изображения out dx,al ; записываем его в счетчик элементов StbDel mov dx,PrtPortCont ; указывает на управляющий порт для счета ;стробов mov al,SetStrobe ; запуск стробирующего импульса out dx,al StbDel mov al,PrtPortContSafe ;конец стробирующего импульса out dx, al SDelay
Структура данных ImageReq 101 mov mov out dx,PrtPortData al,ch dx,al ;указывает на порт вывода данных ; получаем старший значащий бит числа ; элементов изображения ; записываем его в счетчик элементов StbDel mov dx,PrtPortCont ; указывает на управляющий порт mov al,SetHighLow out dx,al StbDel mov al,SetHighLowAndStrobe out dx,al StbDel mov al, SetHighLow ;сброс сигнала Strobe out dx,al StbDel mov al,SetSyncReset ;сброс защелки синхронизации out dx,al StbDel mov al,PrtPortContSafe ; линия HighLow установлена out dx,al ;на низкий уровень SDelay ret SetPixelCount endp ; Процедура .SetPixelCount » ; Вызываемая из языка Си версия предыдущей процедуры ;INPUT: число элементов. ;OUTPUT: отсутствует. ;USES: регистры АХ, СХ, DX. ; С ALL: вызывается из Си. PROTOTYPE: void SetPixelCount (unsigned short). Public .SetPixelCount > .SetPixelCount proc near
102 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4.1. (Продолжение) push Ър mov Ър,sp mov ax,[bp+4] call SetPixelCount pop Ър ret .SetPixelCount endp ; Процедура .GetPicture ;Эта процедура оцифровывает монохромное изображение. Она запоминает ;данные изображения в буфере» адрес которого передается процедуре из языка ;Си. Функция InitializeDigitizer должна вызываться перед вызовом данной ;процедуры» чтобы обеспечить правильную работу программных и аппаратных ;средств. ;INPUT: регистр ВХ содержит указатель типа near на структуру данных ; ImageReq» которая в свою очередь содержит всю информацию» ; необходимую для управления видеопреобразователем. ;OUTPUT: возвращает значение True» если данные видеоизображения помещены ; в буфер. В случае ошибки возвращается значение False. ;USES: все регистры. Сохраняет значения регистров ВР» SI и DI (этого ; требует система ТигЪо С). {CALL: вызывается из Си. ;PROTOTYPE: GetPicture(void). PUBLIC .GetPicture .GetPicture proc near cmp Digit Init »True {определяем» инициализирован ли ; видеопреобразователь je gpl ;если да» то продолжаем работу mov ret ax»False {возвращаем значение False gpl: push push bp si push di {используется в коде на языке С > mov Ъх»ImageReqPtr {помещаем указатель на структуру {ImageReq в ВХ ;все элементы структуры используют ВХ ;в качестве точки отсчета mov mov es»[Ъх].PictBufSeg bp»[bx].PictBufOff ;пара е&:Ър указывает на буфер данных mov mov PColSeg»es PColOff»bp {устанавливаем адрес первого видеостолбца
Структура, данных ImageReq 103 MOV MOV FieldlDone,False FieldlFound,False ;указывает, что полукадр 1 еде не ; обработан и даже еще не найден MOV si,[bx].FirstPixel ; сколько точек в строке смр [bx].HMode,LovRes ;320 ? jne 8P2 ;если нет - переход shl si,l gp2: add si,HBlankingOffset Здесь начинается цикл оцифровки видеостолбца. Регистр SI является счетчиком элементов изображения в видеостроке, а регистр DI - счетчиком видеострок в обрабатываемом изображении. И>3: MOV FieldlDone,False устанавливаем значение FALSE, пока не ; обработаем 1 полукадр MOV di,[bx].FirstLine ; инициализируем счетчик строк MOV es,PColSeg ; адрес текущего видео столбца MOV bp,PColOff > MOV ax,si учитываем показания счетчика элементов call SetPixelCount ; загружаем аппаратные счетчики элементов gp4: смр Fieldl Found, True ; определяем, не является ли обработка ; текущего полукадра повторной gp7 ;если да, переходим по длинному импульсу g₽5: cli ; выключаем прерывания call SyncsPerField ; локализуем 1 полукадр смр ax,SyncsField2 j»P gp6 sti ;включаем прерывания call Delay ;короткая задержка call Delay ;короткая задержка call Delay ;короткая задержка >P short gp5 ;повторяем, пока не найдем начало ;первого полукадра ;Начало первого полукадра найдено. Я>6: MOV FieldlFound,True ;указываем, что начало найдено j*P short gp8 ; Теперь мы должны определить начало каждого последующего четного или ; нечетного полукадра gp7: cli ; выключаем прерывания mov di, [bx] . First Line ; инициализируем счетчик строк call FindLongSync ;находим длинный синхроимпульс ;В данном месте процедуры начало полукадра определено.
104 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4.1. (Продолжение) ;Примечание: сигнал Sync и сигнал сброса ЕОС остаются пока активными, ; поскольку до сих пор мы занимались детектированием исключительно длинных ;(вертикальных) синхроимпульсов. Следующий ниже код считывает порт ввода ;до тех пор, пока его сигнал не перейдет на высокий уровень, что указывает ;на детектирование короткого синхроимпульса. gp8: in al, dx and al,SyncEOC jz gp8 ;переход по низкому уровню сигнала SyncEOC ;Повторная проверка. call Delay ; задержка длительностью 15 мкс in al,dx and al,SyncEOC jz 8P8 ; переход по длинному синхроимпульсу SDelay Первый короткий синхроимпульс определен. Он, скорее всего, является выравнивающим импульсом. Теперь мы долны учесть все выравнивающие импульсы, чтобы определить начало данных изображения. inc dx ; указывает на управляющий порт mov al,PrtPortContSafe ; конец импульса SyncReset out dx,al ;теперь мы можем-с помощью защелки ; детектировать импульсы любой длины SDelay dec dx ; указывает на порт ввода Значение счетчика числа строк игнорируется. Если оцифровывается полнокадровая картина, игнорируется только значение VBlankingOffset. Если же оцифровывается только часть изображения, не учитывается сумма значений VBlankingOf f set + Fir st Line. mov ex,VBlankingOffset Проверка на оцифровку 2 полукадра сюр jne inc FieldlDone,True gp9 ex ;определяем, обработан ли 2 полукадр ;если нет, переход 8Р9: сюр [bx] . First Line, 0 ;определяем строку, с которой начинается ; изображение je gplO ;если строка я 0, переход add ex,[bx].FirstLine ;в противном случае определяем число ; строк, которые следует пропустить gplO: in al,dx учитываем порт ввода and al,SyncEOC ;маскируем все биты, кроме синхронизирующего
Структура, данных ImageReq 105 jnz gplO ; повторяем цикл до обнаружения синхроимпульса SDelay Защелка синхронизации имеет низкий уровень. Необходимо ее сбросить. inc dx ;указывает на PrtPortCont для вывода BOV al, ResetSyncAndEOC устанавливаем высокий уровень сигнала out dx,al ;SyncReset StbDel BOV al,PrtPortContSafe ; конец импульса SyncReset out dx,al SDelay Синхронизирующая защелка сброшена. Необходимо перевести сигнал SyncEOC на высокий (неактивный) уровень, поскольку нам требуется детектирование только коротких синхроимпульсов dec dx ;указывает на PrtPortln dec ex уменьшаем счетчик импульсов jnz gplO ;определяем, все ли импульсы детектированы Все выравнивающие синхроимпульсы детектхфованы. Теперь необходимо установить сигнал GO, чтобы счетчики элементов изображения были запущены возрастающим фронтом синхроимпульса от видеокамеры. inc dx ; указывает на управляющий порт BOV al,SetGo установка бита GO out dx,al SDelay dec dx ; указывает на порт ввода ;Теперь мы находимся в начале видеостроки. Каждая строка должна ;обрабатываться не более 63,5 икс gpll: in al,dx ;поиск ниспадающей кромки синхроимпульса and al, SyncEOC jnz gpll SDelay ; Видеострока начинается ниспадающей кромкой импульса SyncEOC inc dx ; указывает на порт Cont mov al,SetSyncResetAndGо ; сбрасываем защелку синхронизации ;сигнал Go остается высоким, чтобы ; обеспечить запуск счетчика элементов
106 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4*1. (Продолжение) out StbDel dx,al ; изображения возрастающим фронтом ; синхроимпульса MOV out al,SetGo dx,al ; конец импульса SyncReset SDelay Синхронизирующая заделка сброшена, а счетчики элементов изображения запущены. Необходимо установить сигнал SyncEOC на низкий уровень, когда обрабатываемые данные поступят в компьютер dec dx ; указывает на PrtPortln gpl2: in al,dx ; считываем порт ввода MOV cl,al ; копируем данные в регистр CL and al,SyncEOC ;маскируем все биты, кроме ЕОС jnz gp!2 ; продолжаем цикл до окончания потока данных SDelay Теперь мы имеем в регистре CL четыре бита видеоданных следующего вида: 76543210 D D D D ЕОС X X X После сдвига на четыре разряда вправо регистр CL примет следующий вид: 76543210 OOOODDDD (здесь D обозначает данные) shr cl,l shr cl,l shr cl,l shr cl,l Готовимся к приему старших разрядов данных от видеопреобразователя inc dx ; указывает на порт Cont MOV al,SetHighLowAndGo ; устанавливаем сигнал HighLov на высокий ; уровень для получения 2 старших битов ; видеоданных out dx,al ; сбрасываем сигнал ЕОС SDelay dec dx in al,dx Считывание старших битов данных and al,ЗОН ;маскируем все биты, кроме интересующих or cl,al ; получаем 6 бит данных для представления ; текущего элемента изображения
Структура данных ImageReq 107 *ov ев:[bp]>cl gpl3: inc dx nov al,SetGo out dx, al dec dx ; запоминаем эти 6 бит ; указывает на порт Cont устанавливаем сигнал HighLow на ; низкий уровень ;Следует проверить, не пересек ли указатель на записываемые видеоданные ; (регистр ВР) границы сегмента размером 64К. Если это произошло, регистр ;сегмента нужно изменить таким образом, чтобы нулевое смещение внутри ;нового сегмента указывало на следующую ячейку для хранения видеоданных. ;Примечание: при оцифровке изображения в чересстрочном режиме проверка ;конца сегмента может проводиться несколько иным образом. Поэтому ; следующий код выполняется по-разному в зависимости от режима ; оцифровки. смр [bx].VMode,Interlace определяем, работаем ли мы ;в чересстрочном режиме je gpl5 осли да, то переход ;Мы находимся в нечересстрочном режиме (Non Interlace node). спр bp.OFFFFH определяем, достигнут ли конец сегмента jne gpl4 осли нет, то переход MOV ax,es очитываем значение регистра сегмента add ax,SEG0FF ; добавляем смещение MOV es,ax ; снова записываем значение сегмента ;и устанавливаем нулевое смещение Проверяем, закончили ли мы обработку видеостолбца. gpl4: inc Ър inc di смр di,[bx].LastLine jb gptl mov PColSeg,es mov PColOff,bp j»p gp19 ;Мы находимся в чересстрочном режиме Я>15: смр bp.OFFFEH jb gp!6 MOV ax,es add ax,SEG0FF nov es,ax ; инкрементируем указатель на видеоданные ; инкрементируем счетчик строк ;проверяем, не достигнута ли нижняя ; граница изображения ; сохраняем указатель ; переход к процедурам завершения (Interlace node). определяем, достигнут ли конец сегмента ;если нет, то переход ; считываем значение регистра сегмента ; добавляем смещение ; снова записываем значение сегмента ;и устанавливаем нулевое смещение
108 Глава 4. Программные средства, цифрового видеопреобразователя Листинг 4.1. (Продолжение) gpl6: inc bp ; инкрементируем указатель на видеоданные inc bp ;повторяем инкрементирование, чтобы ; получить пространство для записи данных ;из другого полукадра inc di ; инкрементируем счетчик строк mov ax, [bx] . LastLine ; определяем последнюю строку кадра shr ax,l ;делим на два для получения значения ; последней строки полукадра cmp di, ax ;проверяем, не достигнута ли нижняя ; граница полукадра jae gpl7 jmp gpll ;Мы обработали один полукадр видеоизображения в чересстрочном режиме. ;Следует повторить эту операцию для второго полукадра. gpl7: сюр FieldlDone,True ;обработка этого столбца в обоих полукадрах? je gpl8 ;если да, то переход > ;Мы должны вернуться и обработать в другом полукадре mov FieldlDone,True ; установка флага окончания обработки ;полукадра mov es,PColSeg перезагружаем указатель столбца mov bp,PColOff » inc bp ;инкрементируем указатель на видеоданные, ; чтобы просмотреть данйые из другого ;полукадра jmp gp7 учитываем данные второго полукадра gp!8: mov PColSeg,es ; записываем адрес следующего столбца dec bp mov PColOff.bp ; начало данных ;Мы обработали один столбец видеоизображения. Переходим к следующему ;столбцу, независимо от режима оцифровки (Interlace или Non Interlace). gpl9: inc dx ; указывает на порт Cont mov al,PrtPortContSafe ;сбрасываем все управляющие линии out ds, al SDelay sti ;включаем прерывания, поскольку ; хронирование промежутка между ; полукадрами не является критическим сюр [bx] . HMode, LowRes ; определяем разрешение изображения jne gp20 ;Мы обрабатываем изображение размером 320 точек. Необходимо дважды ; инкрементировать счетчик точек для проверки завершения оцифровки
Структура данных ImageReq 109 изображения. Для этого значения счетчиков элементов изображения следует сравнить со значением LastPixel * 2. inc si inc si BOV ax,[bx].LastPixel ;считываем значение LastPixel shl ax, 1 ;и умножаем его на 2 для сравнения add ax,HBlankingOffset свр si,ax ; обработано ли изображение размером 320 ;точек? jae gp21 ;если да, то переход j>P gp3 ;в противном случае переходим к следующему ; виде о столбцу Мы обрабатываем изображение размером 640 точек. gp20: inc si BOV ax,[bx].LastPixel ; определяем номер последнего элемента add ax,HBlankingOffset свр si,ax ;обработано ли изображение размером ; 640 точек? jae gp21 ;если да, то переход jap 8P3 ;в противном случае переходим ;к следующему видеостолбцу ;Оцифровка видеоизображения завершена. Следует подготовиться к возврату ;управления в программу на языке Си. gp21: pop di pop si pop bp mov ax,True ;указывает на отсутствие ошибок выполнения ret _GetPicture endp ; Конец процедур виде о обработки .TEXT ends end Вероятно, наиболее важным элементом данных, имеющимся в структуре дан- ных ImageReq, является PrtBase. Он сообщает драйверу, к какому порту прин- тера физически подсоединен видеопреобразователь. Код драйвера обладает до- статочной гибкостью для того, чтобы работать с любым из трех стандартных портов принтера, поддерживаемых IBM PC. Действительными значениями для этой компоненты являются: LPT1 ОЗВС hex LPT2 0378 hex LPT3 0278 hex
110 Глава 4. Программные средства цифрового видеопреобразователя Для PS/2 моделей 60 и 70 используется значение LPT1. На более старых PC используется адрес LPT2. Видеопреобразователь может быть подсоединен к любому из трех стандартных портов принтера (если ваш PC имеет требуемые аппаратные средства), и программа может управлять каждым отдельно от дру- гих. Когда драйвер получает адрес порта принтера через структуру ImageReq, все другие необходимые адреса портов рассчитываются из него. PictBuf — это указатель типа huge на буфер, в который драйвер должен поместить данные об изображении в цифровом виде. Код на языке Си восприни- мает его как единый указатель, тогда как код низкого уровня рассматривает его как двухбайтовое значение сегмента, за которым следует двухбайтовое значение смещения. Определение структуры ImageReq, используемое кодом на языке Си и кодом ассемблера, отражает это различие. Остальные компоненты в структуре ImageReq управляют конфигурацией по- лучаемого изображения в цифровом виде. Не все конфигурации имеют смысл. В табл. 4.2 приведены некоторые «стандартные» значения для этих параметров конфигурации и получаемые типы изображений. Все конфигурации основыва- ются на стандартных режимах видеодисплея, обеспечиваемых графическим ада- птером VGA. В соответствии с целями книги получение изображения, которое мы не можем вывести на дисплей, представляется бесполезным. Максимальное разрешение видеопреобразователя составляет 640 х 488. Таблица 4.2. Параметры ImageReq (Независимо от разрешения получаемого изображения, на каждый полученный элемент изображения от видеопреобразователя к PC передается 6 бит.) Тип изображения: полученное разрешение 320 х 200 Обычно выводится на дисплей VGA как изображение 320 х 200 В тексте обозначается как изображение низкого разрешения Элемент ImageReq Название Значение HMode VMode FirstLine FirstPixel LastLine LastPixel LowRes NonInterlace 0 0 200 320 В этом режиме оцифровываются 200 из 244 видимых видеострок на единичном полукадре. Остальные 44 видимые видеостроки не воспринимаются видеопреобра- зователем (даже если он в состоянии воспринять их), поскольку нет стандартных видеорежимов, которые могут отобразить эти дополнительные строки без влия- ния на пропорции изображения. Оцифровывается только один полукадр, так как VMode установлен на NonInterlace. Все цветные изображения (без исправления пропорций) получаются с этим разрешением.
Структура данных ImageReq 111 Тип изображения: разрешение 320 х 200 Выводится на дисплей VGA как 100 х 100-элементное изображение В тексте обозначается как изображение типа окно фокуса Элемент ImageReq Название Значение HMode VMode FirstLine FirstPixel LastLine LastPixel LowRes NonInterlace 50 110 150 210 В этом режиме видеопреобразователь получает изображение из 100 элемен- тов в ширину и 100 строк в высоту, центрированное в середине видеоизображения. Поскольку это меньшее изображение может быть получено за меньший промежу- ток времени, оно может быть использовано в методе фокусирования видеокаме- ры; так как полученное изображение обновляется в течение времени, близкого к реальному. Программа окна фокуса представлена при обсуждении программных средств высокого уровня в гл. 5. Тип изображения: разрешение 640 х 200 Обычно выводится на дисплей VGA как изображение 640 х 200 В тексте обозначается как изображение среднего разрешения Элемент ImageReq Название Значение HMode VMode FirstLine FirstPixel LastLine LastPixel HighRes NonInterlace 0 0 200 640 Этот режим подобен режиму низкого разрешения, показанному выше, за ис- ключением того, что. разрешение в горизонтальном направлении удвоено. При этом опять-таки теряются последние 44 строки оцифрованного полукадра. Тип изображения: разрешение 640 х 480 Обычно выводится на дисплей VGA как изображение 640 х 480 В тексте обозначается как изображение высокого разрешения
112 Глава 4. Программные средства цифрового видеопреобразователя Таблица 4.2. (Продолжение) Элемент ImageReq Название Значение HMode HighRes VMode NonInterlace FirstLine 0 FirstPixel 0 LastLine 480 LastPixel 640 Это режим наивысшего разрешения из поддерживаемых как аппаратными средствами видеопреобразователя, так и дисплеем VGA компьютера PS/2. При установленном чересстрочном режиме оцифровываются оба полукадра в видео- кадре. Это дает цифровое представление видеообраза наивысшего качества из возможных для данного видеопреобразователя. Только четыре видимые видео- строки в каждом полукадре в этом методе не воспринимаются. Функции поддержки низкого уровня Ниже обсуждается в общих чертах работа программы получения изображения низкого уровня. Важнейшие понятия мы рассмотрим подробно. Каждая опера- ция снабжена псевдокодом, чтобы дать возможность пользователю увидеть ее функционирование. Для увеличения скорости работы каждая программа была написана на языке ассемблера 80 х 86. Читателю, не знакомому с языком ассем- блера 80 х 86 или с форматом листингов ассемблера, следует обратиться к одной из многих хороших книг по этой теме, перечисленных в списке «Литература». Дальнейшее обсуждение предполагает некоторое знакомство с этими понятиями. Код ассемблера, представленный в листинге 4.1, не обсуждается в тексте строка за строкой. Почти каждая строка кода ассемблера содержит свой соб- ственный комментарий. Поэтому, пожалуйста, обращайтесь к листингу в про- цессе чтения этого раздела. Каждая функция низкого уровня вызывается либо кодом высокого уровня на языке Си, либо главной процедурой получения изображения низкого уровня -GetPicture. Эти функции поддержки непосредственно взаимодействуют с аппа- ратными средствами видеопреобразователя и с аппаратными средствами PC. Мы будем обсуждать процедуры поддержки в том порядке, в котором они находятся в листинге. Термины — процедура и функция — в данном обсуждении использу- ются как синонимы. Все они относятся к одному и тому же: небольшому участку кода (ассемблера или на языке высокого уровня), называемому подпрограммой, который выполняет действие и, возможно, возвращает значение вызывающему коду. Любая процедура языка ассемблера, имеющая приставку _ (например, -GetPicture), может вызываться непосредственно из языка Си. Без этой пристав- ки редактор связей не сможет связать вызов функции из языка Си с требуемой
Функции поддержки низкого уровня 113 функцией на языке ассемблера. Функции (или процедуры), которые не имеют приставки _, могут вызываться только внутри кода ассемблера. Первая функция поддержки InitializeTimer2, используется для инициализа- ции микросхемы таймера/счетчика 8253 в PC. Канал таймера 2 этой микросхе- мы обычно используется для генерирования слышимых тонов, производимых встроенным динамиком PC. Поскольку данная функция не является критиче- ской, этот канал таймера был зарезервирован для видеопреобразователя. Хотя таймер не является необходимым для работы видеопреобразователя, он обес- печивает в некоторой степени независимую от центрального процессора 15-мкс задержку, необходимую для хронирования синхроимпульсов. В частности, тай- мер используется для того, чтобы отличить длинный кадровый синхроимпульс от коротких синхроимпульсов. На менее быстродействующих машинах (IBM PC, PC XT и PC AT) аппаратный таймер не требуется, поскольку выполнение коман- ды доступа к таймеру требует более 15 мкс. Для этих машин 15-мкс задержка может быть обеспечена более эффективно программными средствами. Для полу- чения более полной информации по данному вопросу см. в предыдущем разделе о влиянии быстродействия центрального процессора. Более быстродействующие машины выигрывают от использования аппаратного таймера. Функция InitializeTimer2 устанавливает канал таймера 2 в режим 1, кото- рый эмулирует моностабильный мультивибратор или «однотактный режим». В этом режиме, когда запирающий сигнал переходит с низкого на высокий уро- вень, предварительно установленное значение загружается в счетчик, который начинает считать в направлении убывания. Когда достигается нулевое значе- ние, конечный бит переходит с низкого на высокий уровень. Типичные данные таймера приведены в табл. 4.3. Конструкция этой процедуры следующая: Псевдокод процедуры InitializeTiiner2 Функция - Инициализировать таймер PC для использования преобразователем Входные параметры - Нет Выходные параметры - Нет Статус прерывания - Не имеет значения Начало процедуры InitializeTiiner2 Получение значения режима Timer2 Вывод в командный регистр таймера Получение значения счетчика задержки Timer2 Вывод младшего значащего байта в регистр счетчика таймера Вывод старшего значащего байта в регистр счетчика таймера Окончание процедуры InitializeTiiner Функция InitializeTimer2 сначала записывает режимное слово (В2 hex) для таймера 2 в регистр команд таймера (порт 43 hex), а затем — значение счетчика (задержки) в регистр счетчика таймера 2. Таймер 2 сохранит эту конфигурацию до тех пор, пока она не будет изменена или пока не будет отключено питание PC. В зависимости от частоты задающего генератора, подаваемой на микросхему 8253 PC, значение счетчика, равное 18, создаст задержку около 15 мкс. (Замечание. В более строгой конструкции первоначальное значение счетчика и режим таймера 2 были бы сохранены в памяти для восстановления после завершения работы видеопреобразователя.) Процедура Delay выполняет действительную функцию временной задержки. В основном процедура Delay устанавливает бит затвора таймера 2 и затем вхо- дит в цикл, из которого выйдет, когда активизируется конечный бит счетчика 8-3
114 Глава 4. Программные средства, цифрового видеопреобразователя таймера 2. Поскольку эта процедура вызывается всегда, когда прерывания PC блокируются, мы получаем очень точную временную задержку. Для менее бы- стродействующих PC таймер может исчерпать время еще до того, как будет про- верен конечный бит. По этой причине задержка будет изменяться в зависимости от быстродействия PC. На более медленных PC задержка немного длиннее. Для наших целей любая задержка длительностью от 5 до 25 мкс достаточна для об- наружения кадровых интервалов синхронизации в видеосигнале. Конструкция процедуры Delay следующая: Псевдокод процедуры Delay Функция - Создание 15 мсек задержки (приблизительно) Входные параметры - Нет Выходные параметры - Нет Статус прерывания - Отключен Начало процедуры Delay Установка бита заслонки Timer2 в состояние low и запуск таймера Timer2 Пока бит прерывания Timer2 не установлен Считывание регистра бита прерывания Окончание процедуры Delay Процедура JnitializeDigitizer выполняет три основные функции. Во-первых, она возвращает из кода на языке Си указатель на структуру данных ImageReq, которая определяет изображение, подлежащее оцифровке. Этот указатель типа NEAR возвращается из стека с помощью стандартного метода. Указатель типа NEAR, как вы знаете из гл. 1, является смещением в сегменте данных, исполь- зуемом программой малой модели памяти. Указатель типа NEAR может быть использован, когда сегмент данных памяти задан неявно, что имеет место в на- шем случае. Когда это значение указателя становится известным, может выполняться вторая функция данной процедуры, а именно — извлечение значения PrtBase из структуры данных ImageReq и последующее вычисление других необходимых адресов портов принтера. Эти адреса — PrtPortData, PrtPortln и PrtPortCont — определяют, к какому порту принтера подсоединен преобразователь, и, следовательно, как программа будет взаимодействовать с аппаратными средствами. Эти рассчитанные значе- ния хранятся в определенном месте для использования вместе с кодом низкого уровня видеопреобразователя. Таблица 4.3. Полная информация о таймере 8253 1. Таймер 2 в PC используется для синхронизации видеоимпульсов. 2. Таймер 2 установлен в режим таймера 1, действующего как моностабиль- ный мультивибратор аппаратных средств. 3. Таймер 2 тактирован на 1,19318 МГц. Таким образом, период равен 840 нс. 4. Бит 0 порта вывода 8255 с адресом 61 hex является управляющим битом затвора для таймера 2. 5. Бит 5 порта ввода 8255 с адресом 62 hex является конечным битом счетчика для таймера 2.
Функции поддержки низкого уровня 115 6. Командный регистр таймера с адресом 43 hex используется для установки конфигурации режима таймера. Значение, используемое с видеопреобразо- вателем, составляет В2 hex. Номера Номера битой контрольного слова битов 7__________6________5________4 3 2 1 0 1 0 1 1 0 0 1 0 Номер таймера 00-#0 01 - #1 10 - #2 11 ошибка Считать/Загрузить 1 Режим 0-5 0=bin 1=BCD 00 - счетчик текущей защелки 01 - Читать/Писать только MSB 10 - Читать/Писать только LSB 11 - Читать/Писать LSB, а затем MSB 7. Регистр счетчика таймера по адресу 42 hex записан для установки конечно- го значения таймера 2. Младший значащий байт (LSB) значения счетчика записан первым, за ним следует старший значащий байт (MSB) счетчика. 8. Последовательность операций таймера 2. Таймер 2 активизируется при пе- реходе затвора с низкого на высокий уровень. Значение счетчика таймера 2 перезагружается и таймер 2 начинает считать к нулю. Конечный бит пе- реходит на высокий уровень, когда значение счетчика равно 0. Он остается высоким до тех пор, пока затвор не будет снова открыт. Бит шлюза Бит прерывания 1 счет каждые 840 нс счетчик достигает 0 --------Общая длительность------------►( Инициализация аппаратных средств видеопреобразователя — последняя функция, выполняемая процедурой JnitializeDigitizer. Общий итог инициализа- ции следующий: 1. Все линии управления вывода из порта принтера PC установлены в их неустановленное состояние или состояние покоя. 2. Защелка обнаружения синхроимпульса и защелка ЕОС (конец преобразо- вания) сброшены. 3. Счетчики элементов изображения инициализированы на нулевое значение счетчика. 4. Таймер 2 в PC инициализирован для использования с видеопреобразова- телем. 8*
116 Глава 4. Программные средства цифрового видеопреобразователя 5. Флаг Digitlnit установлен, указывая, что аппаратные средства видеопре- образователя успешно инициализированы. (В действительности он ука- зывает, что эта процедура вызвана до того, как вызывается процедура -GetPicture.) Задержки, распределенные в этой процедуре, находятся в ней для того, что- бы дать возможность как видеопреобразователю, так и аппаратным средствам PC стабилизироваться после доступа к ним. Это предотвращает распростране- ние помех, создаваемых PC, в видеопреобразователе и нарушение его работы. Конструкция функции JnitializeDigitizer следующая: Псевдокод процедуры _InitializeDigitizer Функция - 1) извлекает указатель на ImageReq из Си 2) вычисляет адрес порта принтера 3) инициализирует аппаратуру преобразователя и таймер компьютера Входные параметры - Нет Выходные параметры - Нет Статус прерывания - Не имеет значения Начало процедуры InitializeDigitizer Получение указателя типа NEAR на структуру ImageReq из Си Получение адреса PrtBase из структуры ImageReq Вычисление значений PrtPortData, PrtPortln и PrtPortCont Сохранение всех выходных линий на преобразователе Сброс защелок детектирования синхроимпульсов и "End of Conversion" Сохранение всех выходных линий на преобразователе Устновка счетчика точек в нуль Инициализация таймера2 Установка флага завершения инициализации Окончание процедуры InitializeDigitizer Существуют две версии функции SyncsPerField. Они совпадают между со- бой, за исключением того, что функция JSyncsPerField вызывается прямо из языка Си и используется для проверки правильности работы видеопреобразо- вателя. Другая версия функции SyncsPerField используется внутри процедуры -GetPicture— для определения места старта первого полукадра. Синхронизация этой процедуры и видеосигнала — крайне важная сама по себе — является очень чувствительной по отношению к времени. Поэтому во время выполнения этого кода прерывания должны быть заблокированы. В противном случае во время обслуживания PC прерывающих устройств синхроимульсы могут быть потеря- ны. Эта функция возвращает количество синхроимпульсов, подсчитанное между кадровыми синхроимпульсами (интервалами). Как уже говорилось в гл. 2, первый полукадр видеокадра должен иметь 272 синхроимпульса, тогда как второй полукадр — 271 синхроимпульс. Поэтому только эти два значения должны возвращаться при вызове любой из функций SyncsPerField. Если возвращается значение, отличное от этих двух, синхрони- зирующие макрокоманды, рассмотренные в разделе, касающемся влияния бы- стродействия центрального процессора, должны быть изменены, пока не будут получены требуемые значения (при условии, конечно, что аппаратные средства видеопреобразователя работают правильно).
Функции поддержки низкого уровня 117 Конструкция функции SyncsPerField представлена ниже. Важно понять, что, поскольку кадровые синхроимпульсы относительно длинные (27,1 мкс), для их обнаружения не нужно использовать защелку обнаружения синхроимпульсов в аппаратных средствах видеопреобразователя. Код этой функции отражает дан- ный факт. Все другие синхроимпульсы обнаруживаются с помощью аппаратных средств обнаружения синхроимпульсов, чтобы избежать пропуск импульса. Вы- равнивающие импульсы, например, имеют длительность 2,3 мкс. На более ме- дленных PC обнаружение этих коротких импульсов только программными сред- ствами может оказаться невозможным. Псевдокод процедуры SyncPerField Функция - Подсчет синхроимпульсов в поле видеоизображения Входные параметры - Нет Выходные параметры - Возвращает количество импульсов между вертикальными синхроимпульсами Статус прерывания - Отключен Начало процедуры SyncPerField Установка счетчика возврата в 7. Это количество импульсов будет пропущено Удержание детектора синхроимпульсов и защелки ЕОС в положении off с тем, чтобы вертикальный синхроимпульс мог быть детектирован программой Нахождение интервала короткого синхроимпульса Вызов FindLongSync для того, чтобы определить первый длинный или вертикальный синхроимпульс Определение перехода к первому короткому импульсу после уточнения интервала вертикального импульса Пока синхроимпульс не является вертикальным счетчик +s 1 Возврат значения счетчика Функция FindLongSync возвращает управление вызывающему коду, когда об- наруживается длинный или кадровый синхроимпульс. Все остальные, более ко- роткие синхроимпульсы пропускаются. Эта функция используется для обнару- жения начала нового полукадра. Ее конструкция приведена ниже: Псевдокод процедуры FindLongSync Функция - Определение появления вертикального синхроимпульса Входные параметры - Нет Выходные параметры - Нет Статус прерывания - Отключен Начало процедуры FindLongSync Удержание детектора синхроимпульсов и защелки ЕОС в положении off с тем, чтобы вертикальный синхроимпульс мог быть детектирован программой Цикл Считывание PrtPortln Пока SyncEOC равен high (синхроимпульс не найден) Считывание PrtPortln (обнаружен какой-то синхроимпульс) Задержка до 15 мсек (прошли все короткие импульсы) Повторное считывание PrtPortln (если SyncEOC до сих пор low, детектирован вертикальный синхроимпульс) (если SyncEOC равен high, значит это короткий импульс. Следует продолжать)
118 Глава 4. Программные средства цифрового видеопреобразователя Пока не будет детектирован длинный: синхроимпульс Окончание процедуры FindLongSync Функция SetPixelCount устанавливает защелку счетчика элементов изобра- жения (обычно две аппаратных защелки) для видеопреобразователя. Значение (от 0 до 639), загружаемое в защелку, определяет, какой столбец видеоизобра- жения будет оцифровываться. Защелка счетчика элементов изображения за- гружается программно один раз для каждого столбца оцифрованного видеоизо- бражения. Значение защелки автоматически загружается в счетчики элемен- тов изображения аппаратных средств на падающей кромке каждого строчно- го синхроимпульса, если сигнал Go из PC активен. Возрастающий фронт син- хроимпульса запускает счетчики элементов изображения на убывающий счет к нулю. Защелка счетчика элементов изображения загружается двухшаговой опера- цией, поскольку все 10 бит счетчика должны быть переданы через 8-бит ин- терфейс. Конструкция, приведенная ниже, показывает, как это выполняется. Данный метод был уже рассмотрен выше. Псевдокод процедуры SetPixelCount Функция - Установка счетчика точек, проходимых преобразователем Входные параметры - Желаемое число точек Выходные параметры - Нет Статус прерывания - Не имеет значения Начало процедуры SetPixelCount Вывод младшего значащего байта счетчика точек в порт вывода PrtPortData Данные затем поступают как входящие на защелку счетчика точек преобразователя Переключение бита строба в значение защелки Вывод старшего значащего байта счетчика точек в порт вывода PrtPortData Установка бита High/Lov в high для выбора старшей значащей части защелки счетчика точек Повторное переключение бита строба в значение защелки Сохранить все выходные линии в преобразователе Окончание процедуры SetPixelCount Функция -GetPicture — это основная программная структура для полу- чения изображения. Ее можно рассматривать как некий «драйвер устрой- ства» для видеопреобразователя. При получении команд от кода на языке Си в форме структуры данных ImageReq она будет оцифровывать обозначенное изображение или его часть и помещать результат в буфер памяти. Разреше- ние оцифрованного изображения управляется параметрами, содержащимися в структуре данных ImageReq, как уже говорилось выше. Каждому выбранному элементу изображения соответствует 6-бит значение. Поэтому действительны- ми числовыми значениями для элемента изображения являются значения от 0 до 63. Действие этой функции довольно запутано. Ее код не так структурирован, как остальной, представленный в этой книге, из-за временных ограничений, на- кладываемых на процесс оцифровки. Ключевые моменты, перечисленные в ли-
Функции поддержки низкого уровня 119 стинге и псевдокоде, могут быть неочевидными, но тем не менее очень важны для работы этой функции. 1. При оцифровке в чересстрочном видеорежиме получаемые данные эле- ментов изображения для первого полукадра должны храниться только в четных адресах буфера изображения. Когда оцифровывается второй полу- кадр чересстрочного изображения, данные элементов изображения будут храниться между четными элементами изображения в нечетных адресах буфера. Это обеспечивает правильное чередование данных элементов изо- бражения в буфере изображения. 2. Первая половина строки второго полукадра видеоизображения в черес- строчном видеорежиме отбрасывается. 3. Для каждого элемента оцифровываемого видеоизображения происходит две пересылки данных между видеопреобразователем и PC. Сначала пе- редаются четыре младших значащих (LS) бита видеоданных, а затем — два старших значащих (MS) бита. Они программно комбинируются и обра- зуют 6-бит значение для элемента изображения, выравненное по правой границе байта. Две байтные пересылки необходимы из-за недостатка ли- ний входа в PC. 4. Изображения, оцифровываемые с разрешением выше 320x200, требуют для хранения данных более одного сегмента памяти. Код должен нахо- диться в таком месте, чтобы обеспечивались плавные переходы между сегментами памяти во время оцифровки в режиме реального времени. 5. Видеопреобразователь обрабатывает 320 линий элементов, пропуская все другие части видеоизображения. Этот режим осуществляется исключи- тельно программными средствами низкого уровня. При этом реализуется следующая конструкция: Псевдокод процедуры Get Picture Функция - Преобразует изображение, описываемое в структуре InageReq Записывает данные в буфер, описываемый в структуре InageReq Входные параметры - Нет Выходные параметры - Нет Статус прерывания - Не имеет значения Начало процедуры GetPicture Проверка инициализации преобразователя Выход в случае неправильной инициализации Извлечение буфера изображения из структуры InageReq. Загрузка буфера в регистр ES:BX и сохранение Указывает, что поле 1 еце не обработано Извлечение числа FirstPixel из структуры InageReq Если изображение имеет разрежение LovRes (320 точек) FirstPixel *ж 2 (преобразователь распознает только 640 линий) Добавление горизонтального смецения к числу FirstPixel Цикл (1) (этот цикл преобразует колонку видеоизображения) Указывает, что поле 1 еце не найдено Получение FirstLine из структуры InageReq для преобразования Перезагрузка указателя на буфер изображения в регистры ES:BX из памяти
120 Глава 4. Программные средства цифрового видеопреобразователя Запись желаемого числа в счетчик точек. Это число определяет преобразуемую колонку Если поле 1 еще не найдено Начало Цикл Запрещение прерываний Вызов SyncPerField для получения счетчика синхроимпульсов Если работаем с полем 2 (следующее поле - 1) Окончание цикла Разрешение прерываний Задержка Окончание цикла. Окончание Поле 1 найдено В противном случае поле 1 позиционировано Начало Метка gp7: Запрещение прерываний Загрузка первой линии видеоизображения для преобразования Нахождение вертикального синхроимпульса Окончание (начало обработки позиционированного поля) Нахождение первого короткого синхроимпульса. Это должен быть выравнивающий импульс Получение счетчика вертикального смещения Если обрабатывается изображение с чередованием и обрабатывается поле 2 Добавление 1 к счетчику для чтения полулинии Получение First Line из структуры laageReq Если FirstLine не равен О Проигнорировать линии Если есть пропускаемые линии Ожидание синхроимпульсов Уменьшение счетчика пропускаемых линий Установка линии Go преобразователя, чтобы счетчики точек включались на возрастающем фронте каждого синхроимпульса (теперь начинаем преобразование активной линии) Цикл (2) (для каждой точки в колонке изображения) Ожидание спадающего фронта горизонтального синхроимпульса Сброс защелки обнаружения синхроимпульса и удержание активного сигнала Go Считывание PrtPortln Пока линия SyncEOC не активна Считывание PrtPortln (точки с А по D теперь преобразованы) Смещение 4 LS битов видеоданных на 4 бита вправо Установка бита High/Low для извлечения 2 MS бит видеоданных Считывание 2 MS бит и объединение с 4 LS бит для образования 6 бит видеоданных Запись видеоданных в буфер Сброс бита High/Low (изображение преобразовано и сохранено) Если изображение без чередования
Функции поддержки низкого уровня 121 Начало Проверка байта окончания сегмента памяти буфера изображения Если конец сегмента Перенос значения сегмента на следующий блок 64 Кбайт Увеличение указателя данных на 1 Увеличение счетчика линий на 1 Окончание цикла (2) , когда преобразуется последняя линия Сохранение положения в буфере изображения, где хранится последний байт Окончание Если изображение с чередованием Начало Проверка слова окончания сегмента памяти буфера изображения Если конец сегмента Перенос значения сегмента на следующий блок 64 Кбайт Увеличение указателя данных на 2, чтобы оставить пространство для изображения с чередованием Увеличение счетчика линий на 1 Получение значения LastLine Деление на 2 из-за чередования Окончание цикла (2), когда преобразуется линия LastLine*2 Если данная колонка не преобразована в обоих полях Начало Указывает на то, что поле 1 обработано Перезагрузка указателя буфера на начало данных в буфере Перемещение указателя буфера на 1 для преобразования чередования Переход на метку gp7 Окончание В противном случае Запись адреса, в котором следует сохранить следующую точку Окончание Окончание цикла (2) (обработана колонка видеоизображения. Переходим к следующей колонке точек) Сохранение всех выходных линий преобразователя Если разрежение LowRes (320 точек) Начало Дважды инкрементируем счетчик линий Получение счетчика Last Pixel из структуры ImageReq Умножение на 2 для сравнения Добавление к горизонтальному смещению Окончание цикла (1) если все точки преобразованы Окончание Если разрежение HighRes (640 точек) Начало Увеличение счетчика точек для перехода к следующей колонке Получение значения Last Pixel из структуры ImageReq Добавление к горизонтальному смещению Окончание цикла (1) если все точки преобразованы Окончание Окончание цикла (1) Окончание процедуры Get Picture
122 Глава 4. Программные средства цифрового видеопреобразователя Продолжение тестирования видеопреобразователя Рассмотрев программные средства низкого уровня, мы можем продолжить тести- рование нашего видеопреобразователя. Для продолжения процесса тестирования нам необходима программная поддержка низкого уровня. Как уже говорилось выше, представленные здесь дополнительные тесты требуются только в том слу- чае, когда видеопреобразователь не работает правильно после выполнения всех тестов, описанных в гл. 3. Рассмотрим два дополнительных теста. Первый тест проверяет, могут ли программные средства в PC загружать пра- вильные значения в защелку счетчика элементов изображения. Показателем не- поладок в схеме защелке элементов изображения является оцифрованное изо- бражение, имеющее повторяющиеся части, или некий единообразно построенный участок через все выведенное изображение. Программа для тестирования схемы защелки элементов изображения приве- дена в листинге 4.2. Для успешной работы эта программа должна быть скомпо- нована с файлом digitize.obj. Для проверки правильности работы схемы защел- ки счетчика элементов изображения используются данная программа и тестер. Программа последовательно посылает один высокий сигнал через каждый из десяти выходов защелки. Для проверки наличия правильных уровней сигнала используется тестер. При выполнении программы убедитесь, что желаемый бит действительно идет высоким, а все остальные биты — низкими. Если все 10 бит защелки рабо- тают правильно, можно с уверенностью заключить, что схема защелки счетчика элементов изображения функционирует без ошибок. Если какие-либо из битов не проходят этот тест, тщательно проверьте кабель между PC и видеопреобраг зователем. Используйте тестер для проверки того, что кабель не поврежден и что все 25 сигналов присутствуют на обоих концах. Убедитесь также, что кабель вставлен в разъем порта принтера или параллельного порта, а не последователь- ного. Листинг 4.2. Первая программа тестирования видеопреобразователя. Проверка за- щелки счетчика элементов изображения Ниже приводится содержимое файла testl.prj: digitize.obj testl (digitize.h) Далее следует текст файла testl.с: /***********************************************/ /♦ Тестовая программа видеопреобразователя И 1 ♦/ /♦ разработана в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 08/04/89 ♦/ /***********************************************/ tinclude <stdio.h>
Продолжение тестирования видеопреобразователя 123 tinclude <conio.h> #include “digitize.h" #define MaxBitNum 9 /♦ 10-бит счетчик 0...9 ♦/ /* Эта программа используется для проверки заделки счетчика элементов изображения видеопреобразователя. Каждые 10 бит устанавливаются строго последовательно, так что они могут быть проверены тестером. ♦/ void main( void ) < struct ImageReq Req; /* структура ImageReq */ unsigned BitShift, BitPattern; /♦ Создаем структуру, которая определяет, что будет делать видеопреобразователь. Она передается видеопреобразователю при вызове функции InitializeDigitizer. Она также указывает коду нижнего уровня место присоединения видеопреобразователя. */ Req.ComputeType « PS220; Req.PrtBase » ОхЗВС; Req.Hmode = LovRes; Req.VMode « NonInterlace; Req.NumberOfPasses - 1; Req.Flags - 0L; Req.FirstLine - 0; Req.FirstPixel = 0; Req.LastLine « 200; Req.LastPixel - 320; InitializeDigitizer(AReq) ; /* инициализируем видеопреобразователь */ clrscrO; print!("Digitizer Test Program One - Pixel Counter Latch Test\n\n"); print!(“Each bit from LSB to MSB will be set to a high level\n"); printf(“with all other bits set low.\n"); print!("The output of the pixel latches should be checked with\n"); printfC'with a meter at the following locations on the digit izer\n\n"); printf ("Bit Number - 9 87654321 0\n"); printf ("Device Pin t 1 15 9 10 1 15 9 10 1 15\n"); printf ("Device # |IC12| I--IC11 — I I--IC10 — |\n\n"); printf("A low is < .8 VDC and a high is > 2.5 VDC\n\n"); SetPixelCount( 0 ); /* устанавливаем все биты в ноль */ printf ("All bits of the pixel counter latch should now be low\n"); for(BitShift=O; BitShift <= MaxBitNum; BitShift++) {
124 Глава 4. Программные средства цифрового видеопреобразователя Листинг 4.2. (Продолжение) printf("Press any key to continue\n\n"); getch (); BitPattem = 1 « Bit Shift; SetPixelCount( BitPattem ); printf ("Test bit pattern is: %x\n",BitPattem) ; 1 printf("Test Coupleted\n"); 1 Если некоторые из битов работают, а некоторые не работают, снова проверьте соединения микросхем защелки. Если все в порядке, замените защелки IC14 и IC15. Если соединения выполнены правильно й составные элементы хорошие, у вас не должно быть каких-либо новых проблем с этой схемой. В качестве по- следнего средства подсоедините принтер к порту принтера и проверьте, может ли PC правильно управлять принтером. Конечный тест видеопреобразователя представлен в листинге 4.3 и называется SyncsPerField-тест. Он проверяет рабо- ту схемы обнаружения синхроимпульсов (состоящей из защелки IC8 и вентиля NAND IC9B с тремя входами) и интерфейса РС/видеопреобразователь. Эта те- стовая программа содержится в файлах test2.с и test2.prj. Она также должна быть скомпонована с файлом digitize.obj. Листинг 4.3. Вторая программа тестирования видеопреобразователя. Тест SyncsPer- Field Ниже приводится содержимое файла test2.prj: digitize.obj test2 (digitize.h) Далее следует текст файла test2.c: /###########**######*####*********************#*/ /♦ Тестовая программа видеопреобразователя М2*/ /♦ разработана в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 08/04/89 ♦/ /***********************************************/ /♦ Эта программа используется для проверки обнаружения видеопреобразователем цепочки синхроимпульсов. Она непрерывно показывает, сколько синхроимпульсов зафиксировано в видеокадре. Временные параметры в управляющих кодах нижнего уровня файла digitize.asm будут изменяться, пока не установятся значения 271 и 272. Эта программа выключается нажатием любой клавиши. ♦/ #include <stdio.h> ttinclude <conio.h> #include "digitize.h"
Продолжение тестирования видеопреобразователя 125 void nain( void ) struct InageReq Req; /* структура InageReq */ unsigned Done » FALSE; /* логическое выключение цикла */ clrscrO; printf (’’Digitizer Test Program Two - Sync Detect Circuitry Test\n\n”); /♦ Создаем структуру, которая определяет, что будет делать видеопреобразователь. Она передается видеопреобразователю при вызове функции InitializeDigitizer. Она такие указывает коду нижнего уровня место присоединения видеопреобразователя. ♦/ Req.ComputeType = PS220; Req.PrtBase = ОхЗВС; Req.Hmode = LowRes; Req.VMode ж NonInterlace; Req.NumberOfPasses = 1; Req.Flags = OL; Req.FirstLine = 0; Req.FirstPixel = 0; Req.LastLine = 200; Req.LastPixel = 320; InitializeDigitizer(AReq); /* инициализируем видеопреобразователь */ printf (’’SyncPerField Test\n”); printf (” Hit any key to terminate\n\n”); Done = FALSE; while(!Done) printf (”SPF=7,d\n”, SyncPerFieldO ) ; if (kbhitO) Done = TRUE; Эта программа проверяет не только некоторые из аппаратных средств видео- преобразователя, но также и параметры синхронизации, установленные макро- командами в коде драйвера видеопреобразователя низкого уровня. Неправиль- ная работа аппаратных средств и неверно выбранные параметры синхронизации могут повлиять на результат выполнения этой тестовой программы. Реально эта программа пытается посчитать число синхроимпульсов в полукадре и выве- сти результат на дисплей. Она делает это непрерывно, пока не будет прервана оператором. Если в процессе выполнения постоянно выводятся значения 271 и 272, значит схема обнаружения синхроимпульсов видеопреобразователя работа-
126 Глава 4. Программные средства цифрового видеопреобразователя ет правильно. Если эти значения перемежаются с другими несколько различа- ющимися значениями, значит параметры синхронизации следует немного изме- нить, особенно временные характеристики макрокоманды LDELAY. Если созда- ется впечатление, что тестовая программа зависла, следует проверить уровни сигналов на защелке обнаружения синхроимпульсов IC8. Для каждого синхро- импульса, производимого подсоединенным источником видеоизображения, вы- ходной сигнал защелки должен быть низким. Если это не так, следует внима- тельно проверить соединения. Если выходной сигнал действительно низкий и остается таким, проверьте, подает ли PC сигнал SyncReset, используемый для сброса этой защелки. Если эта тестовая программа постоянно выдает правильные значения, то ваш видеопреобразователь должен работать. Попробуйте оцифровать изображение, используя программы, приведенные в гл. 5. Если у вас все же остаются трудно- сти, для их устранения следует использовать осциллограф. При этом необходимо проверять следующее: 1. Сигнал Go (IC13A, контакт 2) — активный высокий. Позволяет обеспечить постоянную загрузку счетчиков элементов изображения и счет в напра- влении убывания до нуля. 2. Сигнал Cnt О/Start of Conversion (IC4D, контакт 11) — активный высокий для одного генератора при достижении счетчиком элементов изображения нуля. 3. Каждый раз, когда проходит активный сигнал Cnt 0, проходят два им- пульса сигнала генератора от А к D (IC2, контакт 7). 4. После прохождения двух импульсов сигнала генератора от А к D конец защелки преобразования (IC6A, контакт 6) активный низкий. 5. Все защелки сбрасываются соответствующим сигналом из PC. Заключение В главе описаны аппаратные средства видеопреобразователя и его программное обеспечение низкого уровня. Особое внимание уделено вопросам влияния так- товой частоты центрального процессора на работу программных средств низко- го уровня. Подробно обсуждена структура данных ImageReq, которая является интерфейсом между кодом низкого уровня на языке ассемблера и программами высокого уровня на языке Си. Показано, что параметры, помещаемые в струк- туру ImageReq, определяют тип оцифрованного изображения, получаемого ви- деопреобразователем. Наконец, проведено дополнительное обсуждение вопросов тестирования видеопреобразователя с целью выявить последние технические де- фекты в видеопреобразователе, смонтированном пользователем. Теперь, когда аппаратные средства видеопреобразователя полностью протестированы и рабо- тают, программное обеспечение низкого уровня изучено, мы приступаем к обсу- ждению программных средств получения изображения высокого уровня.
Глава 5 Программные средства видеопреобразователя высокого уровня для управления процессом вывода изображения на дисплей В этой главе рассмотрены следующие вопросы: • Качество оцифрованного изображения. • Структура программных средств видеопреобразователя высокого уровня. • Управление видеопреобразователем. • Получение и вывод на дисплей изображений в шкале яркости. • Получение и вывод на дисплей цветных изображений. • Запись полученных изображений в виде графических файлов стандартно- го формата. Качество оцифрованного изображения Перед началом обсуждения демонстрационных программ поговорим о качестве изображения. Старая поговорка, применимая к компьютерным программам, так- же хорошо применима к оцифрованным изображениям: «мусор туда — мусор от- туда». Для получения резкого, качественного оцифрованного изображения тре- буется умение и подготовка. Некоторые факторы, такие как качество видеокаме- ры и соединительного видеокабеля, фокусировка камеры, окружающее освеще- ние и движение объекта, могут неблагоприятно повлиять на качество оцифро- ванных изображений. Установка оборудования, показанного на рис. 5.1, помо- жет добиться максимально высокого качества оцифрованных изображений пу- тем сведения к минимуму перечисленных выше нежелательных факторов (дру- гие факторы ухудшения качества изображений описаны в разделе о программе окна фокуса). Устройство поддержки видеокамеры, называемое копировальным стендом, легко можно собрать из частей, доступных в большинстве магазинов аппаратных средств, или приобретено целиком у фирмы NewTec Inc. (Топека, Канзас, 66603). Схема на рис. 5.1 показывает, как с максимальной эффективностью можно соединить камеру, видеомонитор и видеопреобразователь. Видеопереключатель позволит соединить видеокамеру непосредственно с видеомонитором при уста- новке изображения и фокусировке камеры. Когда на мониторе будет получено
128 Глава 5. Программные средства видеопреобразователя Схематическая диаграмма Дополнительный монитор, используемый для фокусировки камеры, настройки освещения и позиционирования изображения устанавливает настроечный монитор. К схеме Положение В устаналивает преобразователя преобразователь Рис. 5.1. Оборудование, требуемое для оцифровки изображений. требуемое изображение, переключатель устанавливается во второе положение, соединяя видеокамеру со схемой видеопреобразователя. Вместо видеопереклю- чателя можно вручную соединять и разъединять видеокабель между монитором и видеопреобразователем. Когда выполнено соединение и изображение правиль- но ограничено и освещено, видеопреобразователь будет создавать качественные оцифрованные изображения. Вспоминается гораздо более новая поговорка: «Что вы видите, то вы и получаете».
Структура программного обеспечения видеопреобразователя высокого уровня 129 Структура программного обеспечения видеопреобразователя высокого уровня Как описано в гл. 4, программные средства видеопреобразователя низкого уров- ня осуществляют реальную оцифровку видеоизображений. Программные сред- ства высокого уровня должны выполнять все менее критические по времени вспомогательные функции, необходимые для того, чтобы позволить программ- ным средствам низкого уровня беспрепятственно выполнять свою работу. Неза- висимо от типа изображения, которое предстоит оцифровать видеопреобразова- телю, требуются одни и те же основные вспомогательные функции. Фактиче- ски структуры всех оцифровывающих программ, представленных в книге, по- чти идентичны. Каждая программа имеет небольшое отклонение от следующей структуры: Функция получения и отображения оцифрованного изображения Начало Инициализация графической подсистемы VGA Инициализация структуры ImageReq в соответствии с требуемым разрешением изображения Расчет размера требуемых буферов памяти Размещение буферов памяти для хранения данных изображения Инициализация аппаратуры преобразователя путем вызова функции низкого уровня InitializeDigitizer и передачи в нее адреса инициализированной структуры ImageReq Вызов функции низкого уровня GetPicture для получения изображения При необходимости выполнение обработки данных Установка VGA адаптера в нужный режим Установка палитры VGA для отображения Вывод изображения При необходимости сохранение изображения в формате PCX Освобождение буферов памяти Окончание С помощью этой основной программной структуры можно получить мно- го разных изображений. В дальнейшем мы приведем примеры нескольких из многих изобразительных возможностей. Фактически оставшаяся часть этой гла- вы будет посвящена исключительно обсуждению демонстрационных программ, выводящих изображения. Представлен широкий набор демонстрационных про- грамм, начиная с простейшей из возможных программ вывода изображения и кончая программой, которая Выводит оцифрованные изображения с полным на- бором цветов с помощью только черно-белой видеокамеры и диска цветных све- тофильтров. В ч. II книги представлены примеры оцифрованных изображений. Каждое изображение получено с помощью аппаратных и программных средств, обсужда- емых в этой и двух предыдущих главах. Эти изображения показывают, какого качества изображений можно достичь при использовании кода видеопреобразо- вателя высокого уровня, содержащегося в демонстрационных программах, пред- ставленных далее. 9-3
130 Глава 5. Программные средства видеопреобразователя Таблица 5.1. Демонстрационные программы, выводящие изобра- жение Программа вывода изображения общего назначения Эта программа получает изображение с разрешением 320 х200 и затем выводит его на дисплей с 16 или 64 уровнями яркости. Она включает тест SyncsPerField, описанный в гл. 4 для тестирования синхронизации видеопреобразователя. Эта ба- зовая программа идеальна для тестирования работы видеопреобразователя. Про- грамма представлена в листинге 5.1. Программа окна фокуса Эта программа постоянно оцифровывает и выводит небольшую часть изобра- жения, на которую направлена видеокамера. Бе цель — обеспечение вывода на дисплей «окна фокуса», которое обновляется с максимальной скоростью, давая возможность навести и сфокусировать видеокамеру. Изображение окна фокуса выводится на дисплей с разрешением 320 х 200 (даже если полученное изобра- жение гораздо меньше) и 16 уровнями яркости. Эта программа представлена в листинге 5.2. Программа вывода изображения с полным набором разреше- ний Эта программа иллюстрирует все возможные разрешения, которые допускает видеопреобразователь, а также способ записи изображений с различным разре- шением в виде графических файлов PCX. После использования окна фокуса для правильного наведения видеокамеры оцифровывается изображение во весь экран и записывается с разрешениями 320 х 200, 640 х 200 и 640 х 480. Все они выводятся на экран с 16 уровнями яркости перед тем, как будет оцифровано изображение со следующим разрешением, кроме изображения 320 х 200, которое выводится с 64 уровнями яркости. Эта программа представлена в листинге 5.3. Программа вывода оцифрованного изображения с полным на- бором цветов Эта программа оцифровывает цветные изображения с разрешением 320 х 200 в 256 цветах. Чтобы сделать возможными цветные изображения, программа ис- пользует диск цветных светофильтров. Это самая сложная программа видеопре- образователя из представленных в этой книге. Она представлена в листинге 5.4. Программа вывода изображения общего назначения Эта демонстрационная программа, представленная в листинге 5.1, получает от видеопреобразователя изображение с разрешением 320 х 200 и выводит его на дисплей двумя способами. Сначала изображение выводится с палитрой из 16 оттенков серого цвета, а затем снова, но уже с 64 оттенками. Как видно из ли- стинга, эта программа является почти построчной транскрипцией приведенной выше структуры «Получить и вывести на дисплей оцифрованное изображение*. Все изображения с разрешением 320 х 200 в книге используют для вывода на дисплей режим VGA 13Н с 256 цветами. Напомним, что система Turbo С 2.0 не обеспечивает поддержку этого режима VGA, поэтому для его поддержки были
Программа вывода изображения общего назначения 131 разработаны функции ассемблера (подробности см. в гл. 1). Поэтому процедуры вывода на дисплей в данной программе вызывают функцию PutPixel256 вме- сто распространенной функции Turbo С putpixel. Функция PutPixel256 — одна из функций ассемблера, написанная специально для поддержки режима VGA с 256 цветами. Ее назначение и последовательность параметров совпадают с ее аналогом для Turbo С; различие состоит в том, что она поддерживает особый видеорежим. Значение параметра Color, передаваемого функции putpixel, огра- ничено интервалом от 0 до 15 для вывода не более 16 цветов. Интервал параметра Color функции PutPixel256 составляет от 0 до 255 для вывода 256 возможных цветов. Листинг 5.1. Программа вывода изображения общего назначения Ниже приводится содержимое файла gvideo.prj: graphics.lib vgagraph.obj digitize.obj egavga.obj vga (nisc.h pcx.h vga.h) gvideo (nisc.h pcx.h vga.h digitize.h) Далее следует текст файла gvideo.с: /* Программа работы видеопреобразователя ♦/ /* Оцифровывает изображения размером ♦/ /* 320x200. Выводит изображения с 16 ♦/ /* или 64 уровнями яркости ♦/ /* разработана в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли ♦/ /* ♦/ /* Версия: 1.0 ♦/ /♦ Последние изменения внесены 09/14/89 ♦/ /едеееедедееееедедееедееедедееедедедеееедеедееде/ tinclude <stdio.h> finclude <conio.h> t include <process.h> finclude <graphics.h> tindude <alloc.h> tindude "Bisc.h" tindude '’pcx.h” tindude "vga.h" tindude "digitize.h" /* Глобальные переменные ♦/ static struct InageReq Req; static char huge «PictureData; 9*
132 Глава 5. Программные средства видеопреобразователя Листинг 5.1. (Продолжение) /* Эта функция показывает оцифрованное изображение в 16-уровневой палитре. Заметим, что видеоданные в PictData сдвигаются на два разряда вправо. Это масштабирование 6-бит данных к 4-бит необходимо для 16-цветового отображения. Заметим также, что эта функция управляется параметрами из структуры ImageReq. Эти параметры автоматически настраивают DisplayPictData при изменении параметров изображения. ♦/ void DisplayPictDatal6Gray (char huge «PictData) register unsigned Col, Row, Color; unsigned ColSpan, RowSpan; unsigned long PixelBufOffset; ColSpan e Req.LastPixel - Req.FirstPixel; RowSpan e Req.LastLine - Req.FirstLine; for( Col»0; Col < ColSpan; Col++) PixelBufOffset = (long) RowSpan * Col; for( Row=0; Row < RowSpan; Row++) Color « PictData [PixelBuf Off set + Row] ; Color »= 2; /* используем функцию установки точки специально для VGA 13Н необходимую для 320x200 изображения */ PutPixel256(Col,Row,Color); 1 1 1 /♦ Эта функция показывает оцифрованное изображение в 64-уровневой палитре. Заметим, что видеоданные не масштабируются в этом случае, так как все 6 бит необходимы для того, чтобы правильно индексировать цветовой регистр. ♦/ void DisplayPictData64Gray (char huge «PictData) register unsigned Col, Row, Color; unsigned ColSpan, RowSpan; unsigned long PixelBuf Off set; ColSpan “ Req.LastPixel - Req.FirstPixel; RowSpan я Req.LastLine - Req.FirstLine; for (Col=0; Col < ColSpan; Col++) PixelBufOffset = (long) RowSpan ♦ Col; for (Row»0; Row < RowSpan; Row++) { Color « PictData[PixelBufOffset + Row]; /♦ используем функцию установки точки специально для VGA 13Н необходимую для 320x200 изображения ♦/
Программа вывода изображения общего назначения 133 PutPixel256(Col,Row,Color); 1 /♦ основная программа ♦/ void main(void) { unsigned Count; unsigned long RasterSize; InitGraphics() ; /* инициализируем графическую систему ♦/ clrscrO; printf (’’Generic Digitizer Test Program\n\n”) ; printf (’’This program digitizes a 320x200 pixel image then\n”); printf(’’displays it first with 16 and then with 64 levels\n”); printf(”of gray.\n\n”); /* Создаем структуру, которая определяет, что будет делать видеопреобразователь. Она передается видеопреобразователю при вызове функции InitializeDigitizer. ♦/ Req.ComputeType = PS220; Req.PrtBase s ОхЗВС; Req.Hmode = LovRes; Req.VMode = NonInterlace; Req.NumberOfPasses = 1; Req.Flags = 0L; Req. FirstLine = 0; Req.FirstPixel = 0; Req.LastLine = 200; /♦ устанавливаем 320x200 изобр.*/ Req.LastPixel = 320; RasterSize = (Req.LastLine - Req.FirstLine) * (Req.LastPixel - Req.FirstPixel); /♦ запрашиваем память для буфера изображения */ printf (’’Allocating Image Buffer - RasterSize is %lu bytes\n\n”,RasterSize) ; if((PictureData = (char huge ♦) farcalloc(RasterSize, (unsigned long) sizeof (char) ) ) == NULL) printf (’’Digitize - Not enough memory\n”); exit(ENoMemory); /♦ помещаем адрес буфера изображения в структуру ImageReq ♦/ > Req.PictBuf = PictureData; /♦ информируем видеопреобразователь о параметрах изображения */ InitializeDigitizer(AReq);
134 Глава 5. Программные средства видеопреобразователя Листинг 5.1. (Продолжение) /* запускаем тест видеопреобразователя ♦/ printf (“SyncPerField Digitizer Synchronization Test\n\n“); for(Count=0; Count < 10; Count++) printf(“SPF=%d\n“, SyncPerFieldO ) ; printf(“\nPress any key to teminate image display\n\n“); printf(“Acquiring Image . . .\n“); GetPictureO; /* получаем требуемое изображение ♦/ Set256ColorMode(); /* 320x200 256 цветов ♦/ LoadGrayl6Palette(); /♦ загружаем палитру ♦/ DisplayPictDatal6Gray(PictureData) ; /♦ показываем изображение ♦/ get ch (); /♦ оператор выкл. показ ♦/ LoadGray64Palette(); /♦ грузим новую палитру ♦/ DisplayPictData64Gray(PictureData) ; /♦ показываем изображение ♦/ get ch (); /♦ оператор выкл. показ ♦/ restorecrtmodeO; /♦ возвращаем текстовую моду ♦/ f arf гее ((char far *)PictureData); /♦ освобождаем память ♦/ closegraph О ; /♦ закрываем графику ♦/ 1 Две различные функции вывода на дисплей, имеющиеся в этой программе, DisplayPictDatal6Gray и DisplayPict64Gray, различаются только тем, как они определяют масштаб данных изображения, которое они выводят на дисплей. Обе функции выводят свои операционные параметры из содержания структуры «ImageReq». Обе принимают указатель типа HUGE на данные изображения как параметр ввода и обрабатывают данные изображения внутри себя как массив символов. Это делает обработку изображений длиной более 64К байт прозрач- ной для программиста. Это не касается данной демонстрационной программы, поскольку изображения 320 х 200 требуют для хранения точно 64000 байт. Други- ми словами, они не пересекают границу сегмента процессора 80 х 86. Это касается также некоторых других демонстрационных программ. Очень важно отметить, что, когда буфер данных изображения обозначен как массив, индекс должен быть типа long или unsigned long. Он не может быть приведен к типу long, так как в этом случае функции вывода на дисплей не будут работать. Являясь 6-разрядным устройством, видеопреобразователь помещает число- вые значения элементов изображения в интервале от 0 до 63 в буфер данных изображения PictData. Чтобы вывести эти данные только с 16 оттенками серо- го цвета, необходимо изменить масштаб значений и перейти от интервала 0-63 к интервалу 0-15. Изменение масштаба позволит непосредственно использовать данные элементов изображения как индекс в цветовых регистрах VGA. Дру- гими словами, данные изображения при изменении масштаба превращаются в номера цветовых регистров, используемых для вывода элемента изображения. Изменение масштаба может быть легко выполнено путем сдвига данных изо- бражения на две позиции вправо при их извлечении из буфера изображения. Эта методика используется функцией DisplayPictDatal6Gray. Разумеется, при этом предполагается, что цветовые регистры VGA от 0 до 15 уже загружены
Программа вывода изображения общего назначения 135 компонентами цвета 16-уровневой шкалы яркости (с помощью вызова функции LoadGrayl6Palette). Если это не сделано, изображение будет выведено на экран в довольно причудливых цветах, поскольку будут использованы цвета, предо- ставленные BIOS по умолчанию при входе в режим VGA 13Н. Эта методика псевдоцветов может быть использована для получения некоторых специальных эффектов. В данный момент, однако, нас больше интересует правильный вывод на дисплей изображения в шкале 16 уровней яркости. Вторая функция вывода, DisplayPictData64Gray, не использует какого-либо изменения масштаба, поскольку данные элементов изображения уже находятся в подходящем интервале для прямого индексирования цветовых регистров. В этом случае в первые 64 регистра должны быть заранее записаны 64 оттенка серого цвета, чтобы изображение было выведено правильно. Это осуществляется с помощью функции LoadGray64Palette. Обратите внимание на методику извлечения данных изображения из буфе- ра данных изображения, использованную в обеих функциях вывода на дисплей. Вложенные циклы for извлекают данные столбец за столбцом. Это необходи- мо, поскольку последовательные байты буфера данных изображения содержат последовательные строки развертки видеоданных. Это формат, в котором код видеопреобразователя низкого уровня помещает данные в буфер данных изобра- жения (см. рис. 3.5). Изменяя быстро индекс Row, можно осуществить доступ к столбцам последовательно, что и требуется. Необходимо понять это расположе- ние видеоданных, чтобы правильно осуществлять доступ к данным и их вывод на дисплей. Расположение данных в буфере изображения столбец за столбцом диктует заполнение экрана функциями вывода тем же способом. Функция main в этой демонстрационной программе проста. Вызов функции InitGraphics инициализирует графическую систему VGA и регистрирует включе- ние графического драйвера с выполнимой программой. Когда это сделано, про- грамма выдает пользователю ряд сообщений о том, что программа собирается делать. Структура Req, являющаяся копией структуры данных ImageReq, ини- циализируется затем значением, сообщающим коду видеопреобразователя низ- кого уровня, какой тип изображения требуется. Поля ComputerType, Number- OfPasses и Flags не используются, но инициализируются произвольным образом. Все другие поля в структуре Req имеют отношение к нашей демонстрационной программе и поэтому заполняются соответствующими данными. Описание полей структуры данных ImageReq приведено в гл. 4. Затем из информации в структуре данных ImageReq рассчитывается размер необходимого буфера изображения. В этом случае значение RasterSize равно 64000 байт. Программа отражает эту информацию на экране. Затем програм- ма пытается разместить буфер требуемого размера в глобальной памяти. Если память недоступна, программа закончит работу с кодом завершения ENoMemory. Все числовые значения кодов выхода приведены в файле pcx.h. Принимая, что размещение памяти завершено без ошибок, указатель, воз- вращенный процедурой размещения, превращается в указатель типа HUGE и помещается в структуру Req. Код низкого уровня будет располагать данные оци- фрованного изображения в памяти, начиная с этого адреса. Вызов процедуры InitializeDigitizer с адресом структуры Req в качестве па- раметра сообщит коду видеопреобразователя низкого уровня, что ему следу-
136 Глава 5. Программные средства видеопреобразователя ет делать, а также инициализирует аппаратные средства видеопреобразователя для работы. Последующие вызовы процедуры InitializeDigitizer требуются толь- ко тогда, когда необходимо изменить вид работы видеопреобразователя. Для оцифровки одного или тысячи изображений с одним и тем же разрешением не- обходимо вызвать процедуру InitializeDigitizer лишь один раз. Соответствующие примеры приведены в листинге 5.3. Затем выполняется тест SyncsPerField. Этот тест описан в гл. 4. Он не имеет отношения к получению изображения и может быть полностью удален из этой демонстрационной программы без какого-либо ущерба. Он включен сюда для проверки непрерывной работы видеопреобразователя. Как уже говорилось вы- ше, если видеопреобразователь работает правильно, тест SyncsPerField должен выдавать ряд из десяти чисел со значениями 271 или 272. Этим проверяется, верно ли видеопреобразователь синхронизирован с поступающим видеосигналом и может ли он правильно считать синхроимпульсы. Неправильные значения, выдаваемые тестом SyncsPerField, фактически гарантируют неправильную оци- фровку изображений. Безопасный вызов функции GetPicture приводит к тому, что видеопреобра- зователь оцифровывает изображение, на которое направлена видеокамера. Что может быть проще? После окончания этой функции данные изображения со- держатся в буфере, указанном в структуре Req. На этом часть программы, по- лучающая изображение, завершается. Теперь остается только извлечь данные изображения из буфера и правильно вывести их на графический адаптер VGA. Для вывода данных изображения графический адаптер VGA сначала пере- водится в режим 13Н с 256 цветами и разрешением 320 х 200. Затем в цветовые регистры VGA загружается палитра с 16 уровнями яркости с помощью функции LoadGrayl6Palette. Затем вызывается функция DisplayPictDatal6Gray, которая считывает данные изображения из буфера, масштабирует их и выводит на экран элемент за элементом. Конечное изображение с 16 уровнями яркости остается на экране дисплея, пока пользователь не нажмет любую клавишу. После нажатия клавиши повторяется процесс, описанный выше, за исключе- нием того, что в цветовые регистры загружается палитра с 64 уровнями яркости и для вывода на дисплей изображения с 64 уровнями яркости вместо 16 вызы- вается функция DisplayPictData64Gray. Завершение работы программы включает переключение графического ада- птера VGA обратно в текстовой режим, освобождение 64000 байт памяти изо- бражения и закрытие графической библиотеки. Затем происходит выход из про- граммы и управление передается обратно системе DOS. Программа окна фокуса Демонстрационная программа окна фокуса (листинг 5.2) очень похожа на про- грамму вывода изображения общего назначения, представленную в предыдущем примере. Она содержит совсем немного новой, но очень важной информации. Эта программа демонстрирует управление программными средствами видеопре- образователя высокого уровня работой кода низкого уровня и, в конечном сче- те, самого видеопреобразователя. Манипулируя параметрами, передаваемыми из структуры Req коду низкого уровня, можно оцифровать часть видеоизображе-
Программа окна фокуса 137 ния вместо целого. Обратите внимание на значения элементов структуры Req FirstLine, FirstPixel, LastLine и lastPixel в листинге 5.2. Отметим, что их зна- чения определены в терминах разрешения видеопреобразователя, а не дисплея VGA, даже если они в данном случае совпадают. Рассмотрим оцифровку самого центра видеоизображения, когда видеопре- образователь находится в режиме получения изображения низкого разрешения. В этом режиме видеопреобразователь способен оцифровать изображение с 320 элементами по горизонтали и 200 строками по вертикали. Элемент в центре оци- фрованного полукадра будет иметь координаты элемента и строки, равными 160 и 100 соответственно. Если мы решим, что наше окно фокуса должно быть 100 элементов на 100 строк, левый верхний угол окна фокуса будет на элементе 110 в строке 50, а правый нижний угол — на элементе 210 в строке 150. Эти пара- метры передаются в структуре Req. Когда данная структура передается коду низкого уровня с помощью функции InitializeDigitizer, из видеопреобразователя в окно фокуса могут возвращаться почти непрерывные изображения. Функция, используемая для вывода на дисплей изображения окна фокуса DisplayPictData, немного изменена по сравнению с использованной в предыдущем примере, чтобы позволить выводить окно фокуса в любом месте экрана VGA. Два определения ^define в начале программы управляют расположением окна фокуса. Единственным дальнейшим изменением этой демонстрационной программы является цикл while, используемый для получения и вывода на дисплей изобра- жения с максимально возможной скоростью. В тот момент, когда вы смотрите на изображение окна фокуса, видеопреобразователь занят формированием нового изображения. Когда оно готово, окно фокуса обновляется и процесс повторя- ется. Нажатие клавиши на клавиатуре прекращает действие демонстрационной программы таким же образом, как и предыдущей демонстрационной программы. При наблюдении работы этой демонстрационной программы становятся оче- видными два факта. Во-первых, то, что видеопреобразователь рассматривает как центр полу кадра, может отличаться от того, что монитор, подсоединенный к вашей камере, воспринимает как центр. Это можно скомпенсировать с помо- щью регуляторов вертикального и горизонтального размера монитора. Если эти подстройки на вашем мониторе не предусмотрены, параметры, передаваемые со структурой Req, могут быть соответствующим образом сдвинуты, чтобы совме- стить центр изображения монитора с центром изображения видеопреобразовате- ля. Правильные значения сдвига можно определить экспериментальным путем. Во-вторых, оцифрованные изображения, создаваемые видеопреобразовате- лем, не всегда совпадают. При каждом проходе изображение слегка изменяется, иногда напоминая слабое движение оцифровываемого объекта. Если оцифровы- ваемые изображения всегда точно одинаковы, обновление окна фокуса не замет- но. Колебание оцифрованного изображения вызвано шумом в схеме и конструк- ции видеопреобразователя. Все видеокамеры создают некоторый уровень шума, который проявляется в виде ошибок в значениях элементов изображения. Элек- трический шум в соединительных кабелях также вносит свой вклад. Для сведе- ния к минимуму влияния электрического шума используйте между видеокаме- рой и видеопреобразователем экранированный видеокабель высокого качества. Видеокабель должен быть коротким и находиться вдали от всех шнуров питания переменного тока, включая шнуры видеокамеры и осветителей. Если использу-
138 Глава 5. Программные средства видеопреобразователя ются люминесцентные лампы (рекомендуются лампы накаливания), видеока- бель должен располагаться как можно дальше от осветителей. В качестве экспе- римента попробуйте подвигать видеокабель в процессе выполнения программы. Отметьте, как меняются изображения окна фокуса. Для другого интересного эффекта держите постоянный магнит вблизи видеокамеры, когда происходит оцифровка, и смотрите за тем, что происходит. Магнит должен влиять только на более старые видеокамеры, которые используют видикон в качестве прием- ника изображения. Он не должен влиять на камеры, использующие приборы с зарядовой связью. Магнитные поля (вблизи видеокамеры) являются другой формой искажения, непосредственно влияющей на качество изображения. Листинг 5.2. Программа окна фокуса Ниже приводится содержимое файла pvideo.prj: graphics.lib vgagraph.obj digitize.obj egavga.obj vga (misc.h pcx.h vga.h) pvideo (misc.h pcx.h vga.h digitize.h) Далее следует текст файла pvideo.с: /************************************************/ /* Программа окна фокуса видеопреобразователя */ /* разработана в Turbo С 2.0 */ /♦ Крэйгом А. Линдли */ /* */ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 09/14/89 */ /************************************************/ /* Эта программа способна полностью использовать аппаратные средства видеопреобразователя. Она может вызывать видеопреобразователь для оцифровки любой части видеоизображения. Все аспекты оцифровки изображения, от получения до показа, обрабатываются динамически. Структура ImageReq передается кодам нижнего уровня для управления видеопреобразователем. */ # include <stdio.h> «include <conio.h> «include <dos.h> # include <process.h> # include <graphics.h> #include <alloc.h> #include "misc.h" «include "pcx.h" «include "vga.h" «include "digitize.h"
Программа окна фокуса 139 /♦ Определяем два смещения фокусирующего окна, чтобы изображение находилось в центре экрана VGA. ♦/ #define ColCenterOffset 110 tdefine RowCenterOffset 50 /* Глобальные переменные в этом файле */ static struct ImageReq Req; static char huge *PictureData; /* Показ оцифрованного изображения */ void DisplayPictData (char huge *PictData) { register unsigned Col, Row, Color; unsigned ColSpan, RowSpan; unsigned long PixelBufOffset; ColSpan » Req.LastPixel - Req.FirstPixel; RowSpan » Req. Last Line - Req. Fir st Line; for( Col=0; Col < ColSpan; Col++) { PixelBufOffset s (long) RowSpan * Col; for( Row=0; Row < RowSpan; Row++) Color » PictData [PixelBuf Off set + Row] ; Color »« 2; PutPixel256(Col+ColCenterOffset, Row+RowCenterOffset,Color); 1 /* основная программа */ void main(void) { unsigned long RasterSize; InitGraphics () ; /♦ инициализация графической системы */ printf(“Focus Window Test Program\n\n“); printf(“This program will continually digitize and display\n“); printf(“a small portion of the video field\n\n“); /♦ Создаем структуру, которая определяет, что будет делать видеопреобразователь. Она передается видеопреобразователю при вызове функции InitializeDigitizer. ♦/
140 Глава 5, Программные средства видеопреобразователя Листинг 5.2. (Продолжение) Req.ComputeType Req.PrtBase Req.Hmode Req.VMode = PS220; = ОхЗВС; = LowRes; /♦ разрешение 320x200 ♦/ = NonInterlace; Req.NumberOfPasses = 1; Req.Flags Req.FirstLine Req.FirstPixel Req.LastLine Req.LastPixel = 0L; = 50; /* устанавливаем окно фокуса */ » 110; = 150; = 210; RasterSize = (Req.LastLine - Req.FirstLine) ♦ (Req.LastPixel - Req.FirstPixel); /* запрашиваем память для буфера изображения */ printf("Allocating Image Buffer - RasterSize is 7.1u bytes\n\n",RasterSize); if ( (PictureData = (char huge *) fare alloc (RasterSize, (unsigned long) sizeof (char))) == NULL) printf ("Digitize - Not enough memory\n") ; exit(ENoMemory); printf ("Hit any key to terminate program\n\n"); printf("Acquiring Image . . .\n"); Req.PictBuf = PictureData; InitializeDigitizer(AReq); delay(5000); /* позволяем прочитать сообщения */ Set256ColorMode(); /* 320x200 256 цветов */ LoadGrayl6Palette(); /♦ загружаем палитру */ while(!kbhit()) /* непрерывное получение картинки */ { /* пока оператор не нажмет клавишу */ GetPictureO; DisplayPictData(PictureData); get ch () ; /♦ очищаем буфер клавиатуры ♦/ restorecrtmodeO; /* возвращаем текстовую моду */ farfree((char far ♦)PictureData); /♦ возращаем память ♦/ closegraph(); /* закрываем графику */ Программа вывода изображения с полным набором разрешений Эта демонстрационная программа (листинг 5.3) является расширением пер- вых двух программ. Она демонстрирует три степени разрешения изображений,
Программа вывода изображения с полным набором разрешений 141 обеспечиваемые видеопреобразователем. Оцифрованные изображения создают- ся, выводятся на дисплей и по требованию записываются на диск в виде РСХ- файлов с разрешениями 320 х 200, 640 х 200 и 640 х 480. Все изображения вы- водятся на дисплей и записываются в палитре из 16 или 64 уровней яркости. В этой демонстрационной программе используются следующие приемы: а) Примитивный интерфейс пользователя с помощью командной строки DOS. б) Изменение разрешения видеопреобразователя в процессе работы, в) Запись изображений в виде РСХ-файлов. Листинг 5.3. Программа вывода изображения с полным набором разрешений Ниже приводится содержимое файла allvideo.prj: graphics.lib vgagraph.obj digitize.obj egavga.obj pcx (misc.h pcx.h) vga (misc.h pcx.h vga.h) allvideo (misc.h pcx.h vga.h digitize.h) Далее следует текст файла allvideo.с: /ft******************************************/ /* Программа видеопреобразователя */ /* оцифровывает, выводит на экран и */ /* сохраняет изображения во всех режимах */ /* работы видеопреобразователя */ /* Разработана в Turbo С 2.0 */ /* Крэйгом А. Линдли */ /* /* /* Версия: 1.0 Последние изменения внесены 09/28/89 */ */ */ /ффффффффффффффффффффффффффффффффффффффффффф/ tindude <stdio.h> tindude <conio.h> tindude <dos.h> tindude <process.h> tindude <string.h> tindude <graphics.h> tindude <alloc.h> tindude '’misc.h'’ tindude "pcx.h” tindude "vga.h” tindude "digitize.h” tdefine MaxFileNameLen 13 /* длина строки имени файла, включая null */
142 Глава 5. Программные средства видеопреобразователя Листинг 5.3. (Продолжение) /* Определяем два смещения фокусирующего окна, чтобы изображение находилось в центре экрана VGA. */ •define ColCenterOffsetAmount 110 •define RowCenterOffsetAmount 50 /* Глобальные переменные ♦/ struct ImageReq Req; char huge *PictureData; unsigned ColCenterOffset, RowCenterOffset; /* Показ оцифрованного изображения с 64 уровнями яркости */ void DisplayPictDatal (char huge «PictData) { register unsigned Col, Row, Color; unsigned ColSpan, RowSpan; unsigned long PixelBuf Offset; ColSpan = Req.LastPixel - Req.FirstPixel; RowSpan = Req. LastLine - Req. Fir st Line; for (Col=0; Col < ColSpan; Col++) { PixelBufOffset я (long) RowSpan ♦ Col; for (Row=0; Rom < RowSpan; Row++) { Color « PictData [PixelBuf Off set + Row] ; PutPixel256(Col+ColCenterOffset, Row+RowCenterOffset,Color); 1 1 1 /♦ Показ оцифрованного изображения в нормальном 16-цветовом режиме VGA ♦/ void DisplayPictData2( char huge *PictData) { register unsigned Col, Row, Color; unsigned ColSpan, RowSpan; unsigned long PixelBufOffset; ColSpan = Req.LastPixel - Req.FirstPixel; RowSpan s Req.LastLine - Req.FirstLine; for( Col=0; Col < ColSpan; Col++) { PixelBufOffset я (long) RowSpan * Col; for( Rows0; Row < RowSpan; Row++) { Color я PictData [PixelBuf Off set + Row] ; Color »я 2; /♦ масштабируем данные от 0 .. 15 ♦/ putpixel(Col,Row,Color) ; 1 1 1
Программа вывода изображения с полным: набором разрешений 143 Эта функция вцдает подсказку в случае ошибки оператора. Программа выключается после вывода подсказки на экран. ♦/ void ShovHelp( void ) { printf ("\nThis program digitizes, displays and optionally\n"); printf("saves in PCX format a single digitized image in\n"); printf("three different resolutions. Its usage is as follows:\n\n"); printf("allvideo [-o] filename <cr>\n"); printf(" -o creates output PCX files\n"): printf(" filename is name given to the PCX file(s). Do not\n"); printf (" specify a file extension, it Mill be provided.\n\n"); exit(l); /♦ основная программа ♦/ void main(short argc, char *argv[]) { unsigned long RasterSize; unsigned GenPCXFile; unsigned FileNameCounter, Argindex, StrLength; char ♦ImageFil eName; char ProcessesFileName [MaxFileNameLen]; char LRName [MaxFileNameLen]; char NRName [MaxFileNameLen]; char HRName [MaxFileNameLen]; InitGraphics (); clrscrO; printf ("Digitize and Display Gray Scale Images in Many Resolutions\n\n"); /♦ устанавливаем опции ♦/ GenPCXFile s FALSE; /♦ не создавать .PCX-файл ♦/ /♦ анализируем аргументы командной строки*/ FileNameCounter s 0; /♦ число имен файлов, заданных пользователем ♦/ for(Arglndex=l; Argindex < argc; Arglndex++) { if (*argv[Argindex] !=’-’) /♦ если нет ключа в командной строке ♦/ { /♦ должно быть имя файла ♦/ if (*argv[Argindex] =s ’Т’) /♦ требуется помощь ? ♦/ ShovHelpO; if (FileNameCounter > 1) /♦ разрешено только одно имя файла ♦/ ShovHelpO ; /♦ если больше, тогда выход по ошибке*/ ImageFileName = argv [Argindex] ; /♦ сохраняем имя файла изображения */
144 Глава, 5. Программные средства видеопреобразователя Листинг 5.3. (Продолжение) FileNameCounter++; /♦ счетчик для обнаруженной ошибки ♦/ 1 else /♦ ключи командной строки ♦/ s wit ch (*(*argv [Argindex] +1)) /♦ анализируем командную строку ♦/ { case ’o’: /♦ о или О ж выходной файл ♦/ case ’О’: GenPCXFile » TRUE; break; default: printf ("Error - invalid cmd line switch encountered\n"); ShowHelpO; if(GenPCXFile Aft (FileNameCounter != 1)) { printf("Error - single filename required for PCX files\n"); ShowHelpO; /♦ Предобработка любого имени файла, введенного из командной строки. Удаляем любое заданное расширение и ограничиваем длину имени файла максимум 6 символами. Это позволит присоединить LR.PCX, MR.PCX и HR.PCX ♦/ if (GenPCXFile) /* только если необходимо */ { strcpy(ProcessedFileName,""); /♦ пустая строка ♦/ /♦ находим длину имени файла минус расширение ♦/ StгLength я strcspn(ImageFileName,”.; if (StrLength > 6) /♦ превышает макс, длину ? ♦/ stmcat (ProcessedFileName,ImageFileName,6) ; /* берем только 6 символов*/ else stmcat (ProcessedFileName, ImageFileName, StrLength) ; /* Копируем имя обрабатываемого файла в каждое хранящееся имя PCX-файла и добавляем соответствующую строку. ♦/ strcpy(LRName,ProcessedFileName); strcat(LRName,"LR.PCX"); strupr(LRName); strcpy(MRName,ProcessedFileName); strcat(MRName,"MR.PCX"); strupr(MRName); strcpy(HRName,ProcessedFileName); strcat(HRName,"HR.PCX");
Программа вывода изображения с полным набором разрешений 145 strupr(HRName); 1 printf ("Use focus window to focus camera.Then press\n"); printf ("any key to start the digitization process.\n"); printf("Image resolutions are: 320x200,640x200 and 640x480\n\n"); if(GenPCXFile) printf("Image filenames will be:\n\n"); printf (" 7.s for the low resolution 320x200 image\n",LRName); printf (" %s for the mid resolution 640x200 image\n",MRName); printf (" 7.s for the high resolution 640x480 image\n" ,HRName); 1 else printf("PCX files will not be generated\n"); printf ("\nPress any key after third image to terminate program.\n\n"); delay(4000); /* указываем наибольший размер требуемого буфера изображения */ Raster Size = 307200L; /* достаточно для изображения 640x480 */ /* запрашиваем память для буфера изображения */ if ((Pic tur eData = (char huge *) farcalloc (Rast er Size, (unsigned long) sizeof (char))) es NULL) { printf ("Digitize - Not enough memory\n"); exit(ENoMemory); 1 /* Создаем структуру, которая определяет, что будет делать видеохфеобразователь. Она передается видеохфеобразователю при вызове фунвдии InitializeDigitizer. Эта часть структуры ImageReq не изменяется при изменении разрешения изображения. ♦/ Req.ComputeType = PS220; Req.PrtBase ОхЗВС; Req.NumberOfPasses s 1; Req.Flags = 0L; Req.PictBuf = PictureData; /♦ хфисваиваем адрес буфера ♦/ /* Эта часть структуры изменяется хфи изменении разрешения */ Req.Hmode а LowRes; Req.VMode = NonInterlace; Req.FirstLine в 50; /♦ размеры окна фокуса */ ю-з
146 Глава 5. Программные средства видеопреобразователя Листинг 5.3. (Продолжение) Req.FirstPixel Req.LastLine Req.LastPixel « 110; « 150; « 210; InitializeDigitizer(ftReq) ; /* инициализация видеопреобразователя */ Set256ColorMode(); /♦ 320x200 256 цветов */ LoadGray64Palette(); /♦ загружаем палитру ♦/ /♦ смещения фокусирующего окна по отношению к центру экрана */ ColCenterOffset ж ColCenterOffsetAmount; RowCenterOffset « RovCenterOffsetAmount; vhile(’kbhitO) GetPictureO; /* непрерывно получаем картинку */ /* пока оператор не нажал клавишу */ Display?ictDatal(PictureData); getchO; /♦ очищаем буфер клавиатуры ♦/ /* сейчас оцифровываем полное 320x200 изображение */ Req.Hmode Req.VMode Req.FirstLine Req.FirstPixel Req.LastLine Req.LastPixel = LovRes; « NonInterlace; « 0; « 0; = 200; /♦ делаем 320x200 изображение */ « 320; InitializeDigitizer(ftReq); /♦ инициализация видеопреобразователя */ GetPictureO; /♦ получаем картинку */ /* удаляем смещения, так как требуется показать полное изображение ♦/ ColCenterOffset ж 0; RowCenterOffset я 0; DisplayPictDatal (PictureData); /* показываем картинку */ if(GenPCXFile) /* записываем в РСХ-файл ♦/ WritePCXFile(LRName,8,320,200,1,320); /* теперь оцифровываем полное 640x200 изображение */ Req. Hmode Req. VMode Req. LastLine Req.LastPixel ж HighRes; e NonInterlace; я 200; /* делаем 640x200 изображение*/ « 640; InitializeDigitizer(ftReq) ; /* инициализация видеопреобразователя */
Программа вывода изображения с полным набором разрешений 147 GetPicture(); /♦ st egraphmode ( VGALO ) ; LoadGrayl6Palette(); /♦ DisplayPictData2(PictureData); /♦ if(GenPCXFile) /♦ WritePCXFile(MRName,1,640,200,4,80) /♦ теперь оцифровываем полное 640x480 Req.Hmode = HighRes; Req.VMode = Interlace; Req. LastLine = 480; /♦ Req.LastPixel я 640; InitializeDigitizer(ftReq); /* GetPictureO; /♦ stegraphmode(VGAHI); LoadGrayl6Palette(); /♦ DisplayPictData2(PictureData); /♦ if(GenPCXFile) /♦ VritePCXFile(HRName,1,640,480,4,80) getchO; /♦ rest orecrtnodeO; /♦ farfree((char far *)PictureData); closegraph(); } получаем картинку */ загружаем палитру */ показываем картинку */ записываем в РСХ-файл ♦/ изображение */ делаем 640x480 изображение */ инициализация видеопреобразователя */ получаем картинку */ загружаем палитру */ показываем картинку */ записываем в РСХ-файл ♦/ ожидание для оператора */ очищаем экран и выходим ♦/ В этой программе, как и в листинге 5.1, есть две различные функции вы- вода изображения на дисплей, называемые DisplayPictDatal и DisplayPictData2. Функция DisplayPictDatal используется для вывода изображения тогда, когда требуется разрешение 320 х 200 и 64 уровня яркости. Другими словами, когда адаптер VGA находится в режиме 13Н. Функция DisplayPictDatal использует- ся как для вывода окна фокуса, так и для вывода полноэкранного изображения 320 х 200. Функция DisplayPictData2 используется для всех других режимов дис- плея VGA — для вывода изображений с разрешением 640 х 200 и 640 х 480 и 16 уровнями яркости. Действия этих функций вывода изображения рассмотрены в предыдущих демонстрационных программах. Новая функция ShowHelp введена в эту программу как часть механизма ин- терфейса пользователя. При ее выполнении она предоставляет короткое описа- ние использования этой программы, а затем прекращает ее действие. Возвраще- ния к коду, вызвавшему эту функцию, никогда не происходит. Данная функция предоставляет пользователю этой программы единственную доступную помощь. Самый интересный код расположен в части main демонстрационной програм- мы. Здесь вы найдете код, который обрабатывает параметры командной строки, задаваемые пользователем (если хотите, код интерфейса пользователя). Этот ю*
148 Глава 5. Программные средства видеопреобразователя код анализирует командную строку, отыскивая ее ключи (односимвольные ко- манды, следующие за символом «—») и/или имя файла. Как показано в функции ShowHelp, эта программа поддерживает единственный ключ командной строки -о. Этот ключ сообщает программе о том, что необходимо создавать выходные файлы PCX. Если определен ключ -о, анализирующий код командной строки ищет единичное имя файла, также заданное пользователем. Если имя файла не найдено или задано более чем одно имя файла, будет выдано сообщение об ошиб- ке и вызвана функция ShowHelp, напоминающая пользователю, как правильно запускать программу. Следует отметить, что знак вопроса в качестве ключа ко- мандной строки также вызывает текст подсказки. Таким образом, работа этой демонстрационной программы зависит от команд- ной строки, набранной для запуска программы. В случае если allvideo набрано без ключа командной строки -о, программа будет выполняться, но выходные файлы PCX создаваться не будут. Если в каком-либо месте командной строки стоит ключ -о (он может быть до или после имени файла, заданного пользо- вателем), будут создаваться три файла изображения PCX, один для каждого выводимого разрешения. Если в командной строке находится непредусмотрен- ный ключ командной строки, будет выдано сообщение об ошибке и пользователю будет предоставлена подсказка. После выполнения анализирующего кода командной строки переменной GenPCXFile приписывается значение TRUE, если пользователю нужны выход- ные файлы PCX, и FALSE, если не нужны. Переменная GenPCXFile исполь- зуется для выполнения дополнительного кода манипуляции с именами файлов только при условии, что необходимо создавать выходные файлы PCX. Цель этого дополнительного кода — создание единообразных имен файлов PCX для каждо- го разрешения оцифрованного изображения. В общем случае, у имени файла, введенного пользователем, удаляется расширение, если оно было задано, и имя сокращается до максимальной длины 6 символов. Это необходимо, поскольку следующий код дописывает к имени файла определенный признак из двух сим- волов и расширение .PCX. Для низкого разрешения определен признак «LR», для среднего разрешения — «MR» и для высокого разрешения — «HR». Шесть символов усеченного имени файла плюс два символа признака составляют во- семь символов, максимально возможных в DOS. Затем три единообразных име- ни файлов хранятся каждое в своем собственном буфере строки, пока не по- надобятся для создания файлов PCX. Несколько примеров помогут проиллю- стрировать действие этого кода. Если имя файла, заданное в командной строке, было filename.ext, конечные имена, данные файлам PCX, будут FILENALR.PCX, FILENAMR.PCX и FILENAHR.PCX. Обратите внимание, что строка filename была сокращена до 6 символов — шесть filena, после чего были добавлены признак и расширение, а расширение файла, определенное пользователем, полностью удалено. Отметим также, что каждое из образованных имен файлов перед помещением в буфер преобразовано в строки из символов заглавных букв. Если заданное имя файла было junk, ко- нечные имена файлов PCX будут JUNKLR.PCX, JUNKMR.PCX и JUNKHR.PCX. Следует отметить, что код обработки имени файла в этой демонстрацион- ной программе не поддерживает путь DOS. Если путь определен пользователем, программа скорее всего сработает неправильно.
Программа вывода оцифрованного изображения с полным набором цветов 149 После того как выполнен разбор командной строки и все действия со строка- ми, в памяти будет определено место для хранения изображения, как и в других демонстрационных программах. Однако в этом случае объем отведенной памяти не рассчитывается из элементов структуры данных ImageReq. Вместо этого в гло- бальной памяти размещается единый буфер (307200 байт), достаточно большой для изображения с наивысшим разрешением 640 х 480. Этот буфер использу- ется для хранения всех оцифрованных изображений, создаваемых программой. Более мелкие изображения, подобные тем, что используются для окна фокуса, занимают лишь малую часть этого очень большого буфера изображения. После отведения памяти инициализируется определенная часть структуры Req, которая не будет меняться в процессе выполнения демонстрационной про- граммы. Все другие элементы в структуре будут изменяться в зависимости от желаемых разрешений изображения. Начиная с этого момента и до конца демонстрационной программы после- довательно выполняются четыре блока похожего кода: один блок для каждо- го типа принимаемого и выводимого на дисплей изображения. В каждом бло- ке содержится код инициализации структуры Req для требуемого разрешения, для принятия означенного изображения, для установки графического адапте- ра VGA на вывод изображения и для возможной записи изображения в виде файла PCX. Обратите особое внимание на инициализацию структуры Req. По- мните, что функцию InitializeDigitizer необходимо выполнять каждый раз, когда в структуру Req вносятся изменения. Все, что необходимо для записи изображения, выведенного в данный момент на дисплей, в виде файла PCX, — это две строки кода: if (GenPCXFile) WritePCXFile(.......); Если значение GenPCXFile равно TRUE, программой будут создаваться фай- лы PCX. Параметры, передаваемые функции WritePCXFile, очень важны для правильной работы этой функции. Мы не станем сейчас их здесь обсуждать. Данная операция и все другие функции файлов PCX подробно рассмотрены в гл. 6. Функция WritePCXFile является частью представленной в этой главе би- блиотеки функций PCX. Пока же мы используем функцию WritePCXFile без знания того, как она работает. Создаваемые демонстрационной программой файлы PCX могут быть в любой момент выведены на дисплей с помощью программы wiew.exe, обсуждаемой в конце гл. 6. Эти файлы также можно ввести в PC PaintBrush или любую другую графическую программу, принимающую файлы PCX. Файлы изображений PCX, создаваемые демонстрационной программой, имеют практическое применение и вне среды видеопреобразователя. Фактически все изображения, использованные при обсуждении обработки изображений в ч. II книги, были оцифрованы с ис- пользованием этой немного измененной программы. Программа вывода оцифрованного изображения с полным набором цветов Демонстрационная программа 4 (листинг 5.4) создает оцифрованное изображе- ние с полным набором цветов, используя только видеопреобразователь, моно-
150 Глава 5. Программные средства видеопреобразователя хромную видеокамеру и диск цветовых светофильтров. Эта программа для со- здания цветных изображений использует как науку о компьютерах и физику, так и магию. Когда вы увидите действие этой программы и поймете, как она работает, вы сможете оценить, какую роль здесь играет магия. Одно удоволь- ствие видеть точные изображения в полном цвете на дисплее VGA, полученные за такой короткий период оцифровки и обработки. Листинг 5.4. Программа вывода оцифрованного изображения с полным набором цветов Ниже приводится содержимое файла cvideo.prj: graphics.lib digitize.obj vgagraph.obj egavga.obj pcx (misc.h pcx.h) vga (misc.h vga.h pcx.h) cvideo (misc.h vga.h pcx.h digitize.h) Далее следует текст файла cvideo.с: /♦ Программа получения цветных */ /♦ изображений от монохромной видеокамеры */ /* Используется режим 320x200 256 цветов */ /♦ Разработана в Turbo С 2.0 */ /* Крэйгом А. Линдли */ */ */ */ */ ♦/ /* */ /* Использование: */ /* cvideo [-a -d -h -о -р -s -v -х] filename <cr> */ /* Значения переключателей: */ /* -а - поддержка соотношения геометрических размеров ♦/ /* -d - псевдотонирование по методу Флойда - Стейнберга ♦/ /♦ -h или ? - вывод подсказки на экран ♦/ /* -о - вычисление оптимальной палитры */ /♦ -р - создание РСХ-файла ♦/ /* -s - автоматическое масштабирование значений */ /* элементов изображения */ /* -v - вывод информации о работе программы */ /* -х - создание выполнимой программы просмотра */ /♦ ♦/ /* Версия: 1.0 ♦/ /♦ Последние изменения внесены 12/05/89 */ /де***«****««****«**«*****««**де«деде*деде«*де**де*е*де*де*/ ♦/ ♦/ ♦/ */ */ */ */ ♦/ ♦/ ♦/ «include <stdio.h> #include <conio.h> «include <dos.h> «include <alloc.h> «include <process.h>
Программа, вывода, оцифрованного изображения с полным набором цветов 151 «include <graphics.h> «include <string.h> «include <alloc.h> «include <math.h> «include “vga.h” «include "misc.h” «include "pcx.h” «include "digitize.h” /♦ Подробное обсуждение проблемы соотношения геометрических размеров и техники его коррекции см. в гл. 12. В этой программе мы получаем три 320х240-изображения, сжимаем их в 320x200, корректируя соотношение размеров, вызываемое графическим VGA-адаптером. После этого скорректированные изображения используются для создания одного полного цветного изображения. Коррекция соотношения геометрических размеров осуществляется функцией Aspect Correct. ♦/ «define ROWASPECTCORRECTION (double) 1.2 «define SOURCEIMAGEROWS 240 /♦ Биты распределения памяти, используемые для контроля за объемом памяти, выделяемой этой программе. ♦/ «define RGBMEM 1 «define REDMEM 2 «define GREENMEM 4 «define BLUEMEM 8 «define IMAGEMEM 16 unsigned MemoryAlloc = 0; /* переменная, используемая для контроля за выделением памяти ♦/ «define COLLEVELS 32 /♦ число уровней яркости ♦/ «define MAXNUMCOLREGS 256 /* макс, число цветовых регистров */ «define NUMCOLS 320 /♦ размеры изображения ♦/ «define NUMROWS 200 «define NUMAXIS 3 «define NUMPIXELS (long)NUMROWS«NUMCOLS /* полное число точек изображения ♦/ «define SQUARE(х) (x)«(x) union REGS regs; char huge «Red; char huge «Green; char huge «Blue; char huge «ImageBuf; struct ImageReq Req; /♦ указатели для данных изображения ♦/ /* структура требования к изображению */
152 Глава 5. Программные средства видеопреобразователя Листинг 5.4. (Продолжение) /* RGB-куб: трехмерный массив, используемый в тех случаях, когда работа машины с объектами, большими 64К, вызывает затруднения. Индексами в этом массиве являются компоненты цвета (зеленый, красный, синий), нормализованные к масштабу, определяемому COLLEVELS. Значениями массива являются частоты, с которыми встречается определенный цвет. Массив, описанный здесь, - это массив указателей на двумерные массивы, размеры которых не превышают 64К. ♦/ unsigned far *RGBCube [COLLEVELS] ; unsigned NumBoxes; unsigned FloydStein; unsigned OptimumColors; unsigned Verbose; unsigned GenComFile; unsigned GenPCXFile; unsigned ScaleColRegs; unsigned CorrectAspectRatio; /♦ Boxes: Структуры, содержащие не сортированные цветовые блоки. Включает низшее и высшее значения вдоль каждой оси RGB-куба и число элементов в блоке. ♦/ struct Box < unsigned Lo[3]; unsigned Hi[3]; unsigned long NumElements; } Boxes[MAXNUMCOLREGS]; /* Сортированная версия Boxes. ♦/ struct Box SBoxes[MAXNUMCOLREGS]; /♦ ColRegs: содержит определенные значения цветовых регистров. ♦/ struct Regs { BYTE Red; BYTE Green; BYTE Blue; } ColRegs[MAXNUMCOLREGS]; /♦ SColRegs: упорядоченная версия ColRegs. ♦/ struct Regs SColRegs[MAXNUMCOLREGS]; /♦ начало функций, используемых в программе */ /♦ Эта функция выводит переданную строку сообщения, если переменная Verbose равна TRUE. ♦/
Программа вывода оцифрованного изображения с полным набором цветов 153 void Message(char *String) { if(Verbose) printf ("%s", St ring) ; /* Эта функция освобождает всю выделенную память. ♦/ void DeAllocMemory(void) { register unsigned Index; /* Проверяем бит в Memory Alloc, чтобы посмотреть» была ли выделена память */ Message ("Deallocating program memoryXn"); if (MemoryAlloc A RGBMEM) for(lndex=0; Index < COLLEVELS; Index++) if(RGBCube[Index] •= NULL) farfree((unsigned far ♦) RGBCube[Index]); if (MemoryAlloc A REDMEM) farfree((char far *) Red); if (MemoryAlloc ft GREENMEM) farfree((char far *) Green); if (MemoryAlloc A BLUEMEM) farfree((char far *) Blue); if (MemoryAlloc & IMAGEMEM) farfree((char far *) ImageBuf); } /* Выделяет память для всей программы. Переменная MemoryAlloc контролирует всю память, которая будет выделена. Таким образом DeAllocMemory может возвратить системе всю память, когда программа завершена. */ unsigned AllocMemory( void ) register unsigned Index; Message ("Allocating program memory \n"); /* создаем COLLEVELS указателей на двумерные массивы */ /* Устанавливаем первоначальные значения указателей поднассивов RGB-куба в NULL, так чтобы потом посмотреть, была ли выделена память. ♦/ for(Indexs0; Index < COLLEVELS; Index++) RGBCube[Index] « NULL;
154 Глава, 5. Программные средства видеопреобразователя Листинг 5.4. (Продолжение) for(lndex®0; Index < COLLEVELS; Index++) { RGBCube[Index] = (unsigned far *) farcalloc(COLLEVELS«COLLEVELS, (unsigned long) sizeof(short)); if (RGBCube [Index] »= NULL) { printf ("RGBCube memory allocation failed\n"); printf ("\t only %ld bytes of memory available\n" ,farcoreleft ()) ; DeAllocMemoryO; return (FALSE); 1 MemoryAlloc Iе RGBMEM; /* показывает успешно ли ♦/ Red я (char huge *) fare alloc (NUMPIXELS, (unsigned long) sizeof (char)) ; if (Red — NULL) { printf ("Red allocation f ailed\n") ; printf("\t only %ld bytes of memory available\n",farcoreleft()); DeAllocMemoryO; return (FALSE); 1 MemoryAlloc Is REDMEM; /* показывает успешно ли */ Green e (char huge *) fare alloc (NUMPIXELS, (unsigned long) sizeof (char)) ; if (Green e= NULL) printf ("Green allocation failed\n"); printf ("\t only %ld bytes of memory available\n",farcoreleft0); DeAllocMemoryO; return (FALSE); } MemoryAlloc Is GREENMEM; /* показывает успешно ли */ Blue “ (char huge *) farcalloc(NUMPIXELS,(unsigned long) sizeof (char)) ; if (Blue »» NULL) { printf ("Blue allocation f ailed\n") ; printf("\t only %ld bytes of memory available\n">farcoreleft()); DeAllocMemory(); return (FALSE); MemoryAlloc Iя BLUEMEM; /* показывает успешно ли */ /* запрашиваем буфер памяти, в который будут помещены все три изображения */ ImageBuf»(char huge *) farcalloc(320L*240L, (unsigned long) sizeof (char)); if (ImageBuf == NULL) { printf ("Image buffer allocation failed\n");
Программа вывода оцифрованного изображения с полным набором цветов 155 printf("\t only 7.1d bytes of memory available\n",farcoreleft()); DeAllocMemoryO; return (FALSE); 1 Memory Alloc |= IMAGEMEM; /♦ показывает успешно ли */ return(TRUE); /♦ Эта функция располагает входные данные в диапазоне даваемой COLLEVELS. Когда она выполнена, все описанные массивы содержат значения между О и COLLEVELS-1 включительно. */ void Normalsize (char huge *ColorData) register BYTE Min, Max; unsigned long Longlndex; /* находим максимальное и минимальное значения в массиве */ Min » 0; Мах = 0; for(LonglndexM);Longlndex < NUMPIXELS; Longlndex++) if(ColorData[Longlndex] < Min) Min s ColorData [Longlndex] ; if (ColorData[Longlndex] > Max) Max ColorData[Longlndex]; /* Ставим каждую точку в диапазон 0 ... COLLEVELS ♦/ for(LonglndexM);Longlndex < NUMPIXELS; Longlndex**) ColorData[LongIndex] = ((ColorData[Longlndex]-Min)*(COLLEVELS-1))/ (Max-Min); } /* Эта функция заполняет массив RGB-куб частотой, с которой встречается данный цвет, и, как побочный результат, считает и печатает число различных цветов при вводе. ♦/ void ScanColorFrequencies( void ) { register unsigned c, k, NumColors; unsigned long Longlndex; /* Инициализируем RGB-куб ♦/ for(c«0; c < COLLEVELS; c++) for(k=0; k < COLLEVELS; k++) RGBCube[c][k] s 0; /* Для каждой точки считаем число точек с этим цветом */ for(LondIndexs0; Londlndex < NUMPIXELS; Londlndex++) RGBCube [Red[Londlndex]] [(Blue [Londlndex] *COLLEVELS) +Green [Londlndex]] ++;
156 Глава 5. Программные средства видеопреобразователя Листинг 5.4. (Продолжение) /* Считаем и печатаем число различных цветов на входе» сканируя массив RGB-куб и проверяя его на ненулевую частоту. ♦/ NumColors » 0; for(c=0; с < COLLEVELS; с++) for(k=0; k < COLLEVELS; к++) if(RGBCube[с][к]) NumColors++; if(Verbose) printf ("%d unique colors in image data\n",NumColors) ; 1 /* Эта функция ставит индексы тем осям» которые были отмечены после главной. ♦/ void Other Axes (unsigned mainaxis,unsigned ♦other 1» unsigned ♦other2); switch(mainaxis) case 0: ♦otherl « 1; ♦other2 = 2; break; case 1: ♦otherl = 0; ♦other2 s 2; break; case 2: ♦otherl = 0; ♦other2 = 1; 1 1 /♦ Эта функция берет значение индекса в массиве блоков и сжимает специфицированный блок вокруг входной частоты цвета. ♦/ void Shrink (unsigned Boxindex) unsigned axis»aaxl»aax2; register unsigned ind[3], flag; /♦ Вдоль каждой оси ♦/ for(axis=0;axis < NUMAXIS;axis++) OtherAxes(axis»ftaaxl»ftaax2); /♦ Сканируем нулевую плоскость от нижнего конца оси ♦/ flag=0;
Программа, вывода оцифрованного изображения с полным набором цветов 157 for(ind[axis]«Boxes[Boxindex].Lo[axis]; ind[axis] <« Boxes [Boxindex] .Hi [axis] ; ind[axis]++) < for(ind[aaxl]«Boxes [Boxindex] . Lo [aaxl] ; ind[aax 1] <= Boxes [Boxindex] . Hi [aax 1] ; ind[aaxl] ++) < f or (ind [aax2] «Boxes [Boxindex] . Lo [aax2] ; ind[aax2] <« Boxes [Boxindex] . Hi [aax2] ; ind[aax2] ++) if(RGBCube[ind[0]][ind[1]*COLLEVELS+ind[2]]) < flag«l; break; 1 if(flag) break; } if(flag) break; } Boxes[Boxindex].Lo[axis] « ind[axis]; /♦ Сканируем нулевую плоскость от верхнего конца оси ♦/ flag « 0; for(ind[axis]«Boxes[Boxindex].Hi[axis]; ind[axis]+1 >« Boxes[Boxindex].Lo[axis]+1;ind[axis]—) for(ind[aaxl]«Boxes[Boxindex].Hi[aaxl]; ind [aaxl]+1 >= Boxes [Boxindex] .Lo [aaxl]+1; ind [aaxl]—) < f or(ind[aax2]«Boxes[Boxindex].Hi[aax2]; ind [aax2]+1 >= Boxes [Boxindex] . Lo [aax2] +1; ind [aax2]—) if (RGBCube [ind[0] ] [ind[l] *C0LLEVELS+ind[2]] ) < flag«l; break; 1 if(flag) break; } if(flag) break; 1 Boxes [Boxindex] . Hi [axis] « ind [axis] ; 1 } /♦ функция-отладчик печати блока ♦/ void PrtBox (unsigned Boxindex) < printf ("\nBox Number %d\n“,Boxindex); printf(“Hi[0]=7.d Lo[0]=%d\n“,Boxes[Boxindex].Hi[0],Boxes[Boxindex].Lo[0]); printf(“Hi[l]=%d Lo[l]=%d\n“,Boxes[Boxindex].Hi[l],Boxes[Boxindex].Lo[l]); printf("Hi[2]=%d Lo[2]=%d\n“,Boxes[Boxindex].Hi[2],Boxes[Boxindex].Lo[2]); printf (“Elements %ld\n\n“,Boxes [Boxindex] .NumElements) ; getch() ;
158 Глава 5. Программные средства, видеопреобразователя Листинг 5.4. (Продолжение) /♦ Эта функция выбирает оптимальные цвета из данных частоты цвета, используя Median Cut алгоритм. При выходе печатает число используемых цветов. ♦/ void SelectColorBoxes( void ) register unsigned SelectedBox,c; register unsigned ind[3], Max, axis, TargetBox, k; unsigned aaxl, aax2; unsigned long LongMax, PlaneSum, Element Sum; /♦ Инициализируем только первый блок в массиве, чтобы содержать весь RGB-куб, затем сбрасываем неиспользуемые нулевые плоскости, окружающие куб. ♦/ for(c»0; с < NUMAXIS; C++) Boxes [0] . Lo [с] « 0; Boxes[0].Hi[с] « COLLEVELS-1; Boxes [0]. NumElements = NUMPIXELS; NumBoxes = 1; Shrink(0); /♦ Выполняем следующее, пока не будут использованы все цветовые регистры ♦/ while(NumBoxes < MAXNUMCOLREGS) /♦ Для дальнейшей работы выбираем блок с максимальным числом элементов, который содержит не одно значение цвета. Этот блок мы будем расщеплять. ♦/ LongMax = 0; SelectBox « 1000; for(cs0; с < NumBoxes; с++) if ((Boxes [с] .NumElements > LongMax) && ((Boxes[c].Lo[0] != Boxes[c].Hi[0]) II (Boxes[c].Lo[1] != Boxes[c].Hi[1]) II (Boxes [c] . Lo[2] != Boxes [c] . Hi [2] ) ) ) LongMax « Boxes[c] .NumElements; SelectedBox = c; 1 1 /♦ Если мы не нашли блок с не единственным цветом, можем выключать этот цикл. ♦/ if(SelectedBox == 1000) break; /* Выбираем длиннейшую ось блока, чтобы расщепить вдоль */ axis = 0;
Программа вывода, оцифрованного изображения с полным набором цветов 159 Мах = Boxes[SelectedBox].Hi[axis] - Boxes[SelectedBox].Lo[axis]; for(k“l; к < NUMAXIS; k++) if(Max < (c* (Boxes[SelectedBox].Hi[k]- Boxes[SelectedBox].Lo[k]))) Max = c; axis = k; /♦ Смотрим» любой ли из наших первоначальных блоков имеет нулевое число элементов (может произойти вырожденный случай). Если это имеет место» снова используем блок. В противном случае используем следующий имеющийся в распоряжении блок. ♦/ TargetВох “ NumBoxes; for(c=0; с < NumBoxes; с++) { if(Boxes[с].NumElements »» 0) TargetВox « c; break; OtherAxes(axis»&aaxl»&aax2); if (Boxes [SelectedBox] . Hi [axis] ! “ Boxes [SelectedBox] . Lo [axis] ) /♦ Суммируем плоскости блока с нижнего конца пока сумма не превысит половину общего числа элементов в блоке. ♦/ ElementSum “ 0; for(ind[axis]“Boxes[SelectedBox].Lo[axis]; ind [axis] <“ Boxes[SelectedBox].Hi[axis]; ind [axis]++) { PlaneSum = 0; for (ind [aaxl]“Boxes[SelectedBox].Lo[aaxl]; ind [aaxl] <= Boxes[SelectedBox].Hi[aaxl]; ind[aaxl]++) for(ind[aax2]“Boxes[SelectedBox].Lo[aax2]; ind[aax2] <= Boxes [SelectedBox].Hi[aax2]; ind[aax2]++) PlaneSum +“ RGBCube[ind[O]][ind[l]*C0LLEVELS+ind[2]]; Element Sum +“ PlaneSum; if (ElementSum > Boxes[SelectedBox].NumElements/2) break; 1 /♦ Если сумма не превышает половины общего числа элементов до обработки последней плоскости (такое случается» когда последняя плоскость содержит основную массу данных)» следует создать копию блока. */
160 Глава 5. Программные средства видеопреобразователя Листинг 5.4. (Продолжение) if(ind[axis] яя Boxes[SelectedBox].Hi[axis]) < ind [axis] —; Element Sun -= PlaneSum; } /* Новый блок содержит те же данные, что и предыдущий блок, за исключением того, что младший элемент теперь является индексом точки, в которой требуется произвести расщепление. Кроме того, общее число элементов в новом блоке равно общему числу элементов во всем блоке за вычетом уже просуммированных плоскостей. ♦/ for(c=0; с < NUMAXIS; с++) < Boxes[TargetBox].Lo[с] а Boxes[SelectedBox].Lo[с]; Boxes[TargetBox].Hi[c] я Boxes[SelectedBox].Hi[c]; Boxes[TargetBox].Lo[axis] я ind[axis]+1; Boxes[TargetBox].NumElements = Boxes[SelectedBox].NumElements - ElenentSun; /♦ Предыдущий блок теперь разделен по плоскости, которую мы только что расщепили. Он содержит просуммированное число элементов. */ Boxes[SelectedBox] .Hi[axis] я ind[axis] ; Boxes[SelectedBox] .NumElenents = ElenentSun; /* Удаление нулевых плоскостей вокруг новых блоков.*/ Shrink(SelectedBox); Shrink(TargetBox); /* Если мы использовали самый верхний блок в списке, следует увеличить на единицу общее число использованных блоков, для того чтобы быть готовыми к использованию следующего свободного блока. */ if(TargetBox == NumBoxes) NumBoxes++; /* вывод по требованию количества используемых цветов */ if(Verbose) printf (”%d colors Bill be used for display of the image\n",NumBoxes); /* Функция SortColors рассчитывает действительные, значения цветовых регистров для каждого блока на основании взвешенного распределения данных в этом
Программа вывода оцифрованного изображения с полным набором цветов 161 блоке. Затем она сортирует регистры в зависимости от яркости (используя метод расчета, описанный в документации по VGA, для определения яркости). ♦/ void SortColors( void ) register unsigned Index,c,flag,temp,r,b,g; unsigned indices[MAXNUMCOLREGS]; unsigned long weightedcolor [MAXNUMCOLREGS] , rsum, bsum, gsum, tmp; for(Index«0; Index < NumBoxes; Index++) /♦ Расчет взвешенной суммы значений цвета для блока. ♦/ rsum = bsum ® gsum ® 0; for(r^Boxes[Index].Lo[0];r<=Boxes[Index].Hi[0];r++) for(b«Boxes[Index].Lo[1];b<=Boxes[Index].Hi[l];b++) for(g=Boxes[Index] .Lo[2] ;g<=Boxes[Index] .Hi[2] ;g++) tmp « RGBCube [r] [b*COLLEVELS+g] ; rsum += r*tmp; bsum +a b*tmp; gsum += g*tmp; 1 /♦ Выбор действительного значения цвета для данного блока на основании полученной суммы. ♦/ ColRegs [Index] . Red ® rsum/Boxes [Index] . NumElements; ColRegs [Index] . Blue = bsum/Boxes [Index] . NumElements; ColRegs[Index].Green e gsum/Boxes[Index].NumElements; 1 /♦ Установка сортировки по яркости для начального расчета взвешенной яркости каждого цвета. ♦/ for(Index«0; Index < NumBoxes; Index++) indices [Index] e Index; weightedcolor[Index] = ColRegs[Index].Red *30 + ColRegs[Index].Blue *11 + ColRegs[Index].Green*59; 1 /♦ Сортировка взвешенных цветов по их индексам. Сортировка проводится в возрастающем порядке. ♦/ flag = 1; while (flag) flag = 0; for(lndex=0; Index < NumBoxes-1; Index) 11-3
162 Глава 5. Программные средства видеопреобразователя Листинг 5.4. (Продолжение) if (weightedcolor[indices [Index]] > weight edcolor [indices [Index* 1] ] ) temp = indices [Index]; indices [Index] • indices [Index+1]; indices [Index+1] = temp; flag = 1; 1 1 /♦ Модификация блоков и цветовых регистров в значения SBoxes и SColRegs с помощью найденных индексов. ♦/ for(lndex=0; Index < NumBoxes; Index**) SColRegs[Index].Red я ColRegs[indices[Index]].Red; SColRegs[Index].Blue я ColRegs[indices[Index]].Blue; SColRegs[Index].Green я ColRegs[indices[Index]].Green; SBoxes [Index] .NumElements я Boxes [indices [Index]] .NumElements; for(c=0; c < NUMAXIS; C++) SBoxes[Index].Hi[c] = Boxes[indices[Index]].Hi[c]; SBoxes [Index] . Lo [c] = Boxes [indices [Index] ] . Lo [c] ; 1 1 1 /* Получение значения цвета определенной точки на экране VGA-ионитора. ♦/ unsigned Get Pixel Value (unsigned long PixNum); register unsigned Col, Row; Col = (unsigned) (PixNum / (long)NUMROWS) ; Row = (unsigned) (PixNum % (long)NUMROWS) ; return(GetPixel256(Col,Row)); 1 /* Установка значения цвета определенной точки на экране VGA-ионитора. ♦/ void SetPixelValue(unsigned long PixNum, unsigned Value); register unsigned Col, Row; Col я (unsigned) (PixNum / (long)NUMROWS) ; Row я (unsigned) (PixNum % (long)NUMROWS) ; PutPixel256(Col,Row,Value); 1 /♦ Данная функция переводит данные об элеиентах изображения, находящиеся в массиве ввода, в новую цветовую карту. Кроне того, функция иожет использовать псевдотонирование по иетоду Флойда - Стейнберга для
Программа, вывода оцифрованного изображения с полным набором цветов 163 уменьшения ошибок преобразования изображения. ♦/ void DisplayImageData( void ) register unsigned c,k,goodindex,PixVal; unsigned long minerror,error,Londlndex; register int RedDif ,GreenDif ,BlueDif ,r,g,b,i; /♦ Запись в массив RGBCube заранее неверных значений (MAXNUMC0LREGS*2) для того, чтобы мы могли определить область массива, не входящую в цветовой блок. ♦/ for(c=0; с < COLLEVELS; C++) for(k=0; k < COLLEVELS*COLLEVELS; k++) RGBCube[c][k] = MAXNUMC0LREGS*2; /♦ Заполнение блоков в массиве RGBCube значениями индексов для этого блока таким образом, чтобы обеспечить оптимальный доступ к цветовой информации. ♦/ for(i=0; i < NumBoxes; i++) for(r=SBoxes[i].Lo[0]; r <= SBoxes[i].Hi[0]; r++) for(b=SBoxes[i].Lo[l] ; b <= SBoxes[i].Hi[1]; b++) f or(g=SBoxes[i] . Lo[2] ; g <= SBoxes[i] .Hi[2] ; g++) RGBCube[r][b*COLLEVELS+g] = i; /* для каждого элемента изображения */ for(LongIndexB0; Longlndex < NUMPIXELS; Longlndex++) /♦ Если значения цвета для данной точки лежат в допустимом диапазоне, а сам цвет находится внутри одного из блоков, следовательно имеющийся цветовой режим не является оптимальным. ♦/ if(Red[Longlndex] < COLLEVELS ftft Blue[Longlndex] < COLLEVELS ftft Green[Longlndex] < COLLEVELS && (RGBCube[Red[Longlndex]][Blue[Longlndex]*COLLEVELS+Green[Longlndex]] !=MAXNUMC0LREGS*2) && !OptimumColors) PixVal RGBCube[Red[Longlndex]][Blue[Longlndex]*COLLEVELS+ Green[Longlndex]]; SetPixelValue(Longlndex,PixVal); 1 else /♦ В противном случае мы должны найти в массиве цвет, наиболее близкий к заданному. ♦/ goodindex я 0; и*
164 Глава 5, Программные средства видеопреобразователя Листинг 5.4. (Продолжение) minerror » SQUARE(Red[Longlndex]-SColRegs[goodindex].Red)+ SQUARE(Blue[Longlndex]-SColRegs[go odlndex].Blue)+ SQUARE (Green [Longlndex]-SColRegs [goodindex] .Green) ; /♦ Следует просмотреть все цветовые регистры и определить из них один, дающий минимальную ошибку. Этот регистр и будет использован для данного элемента изображения. ♦/ for(k»l; k < NumBoxes;к++) error » SQUARE (Red [Longlndex] -SColRegs [k] .Red) + SQUARE(Blue[Longlndex]-SColRegs[k].Blue)+ SQUARE(Green[Longlndex]-SColRegs[k].Green); if(error < Binerror) minerror = error; goodindex я k; 1 1 /♦ Устанавливаем значение цветового регистра для данного элемента. ♦/ SetPixelValue(Longlndex,goodindex); /♦ при необходимости выполняем псевдотонирование ♦/ if(FloydStein) /* Определение разности между действительным цветом данного элемента изображения и значением цветового регистра, приписанного данному элементу. ♦/ RedDif я ((int)SColRegs[GetPixelValue(Longlndex)].Red) - ( ( int) Red [Longlndex] ) ; BlueDif = ((int)SColRegs[GetPixelValue(Longlndex)] .Blue) - ( ( int) Blue [Longlndex] ) ; GreenDif » ((int)SColRegs[GetPixelValue(Longlndex)] .Green) - ( (int)GreenLonglndex] ) ; /♦ если мы не дошли до правой границы изображения ♦/ if ((((Longlndex+NUMROWS) / NUMROWS) < NUMCOLS) && (Longlndex+NUMROWS) < NUMPIXELS) /♦ Распределяем 3/8 ошибки на элемент изображения справа от нас ♦/ Red [Longlndex+NUMROWS] += (RedDif *3) /8; Blue [Longlndex+NUMROWS] +» (BlueDif*3)/8; Green [Longlndex+NUMROWS] +я (GreenDif*3)/8; /♦ если это привело к переходу правой от нас точки ♦/ if (Red [Longlndex+NUMROWS] > COLLEVELS-1 II Green [Longlndex+NUMROWS] > COLLEVELS-1 | I Blue[Longlndex+NUMROWS] > COLLEVELS-1)
Программа вывода оцифрованного изображения с полным набором цветов 165 /* не обрабатываем дальше этот элемент ♦/ Red[LongIndex+HUMROWS] — (RedDif*3)/8; Blue[Longlndex+NUMROWS] — (BlueDif*3)/8; Green[Longlndex+NUMROWS] -» (GreenDif*3)/8; /♦ если мы не находимся на нижней границе изображения */ if(Longlndex % HUMROWS < NUMROWS-1) { /* Распределяем 3/8 ошибки на нижний элемент изображения */ Red[LongIndex+1] += (RedDif*3)/8; Blue[LongIndex+l] += (BlueDif*3)/8; Green[LongIndex+l] +« (GreenDif*3)/8; /♦ если это привело к переходу нижней от нас точки */ if(Red[LongIndex+1] > COLLEVELS-1 11 Green[LongIndex+1] > COLLEVELS-1 |I Blue[Longlndex*1] > COLLEVELS-1) { /* не обрабатываем дальше этот элемент ♦/ Red[LongIndex+1] (RedDif*3)/8; Blue[LongIndex+l] -= (BlueDif*3)/8; Green[Longlndex+l] -“ (GreenDif*3)/8; } /♦ если не достигнут правый нижний угол изображения */ if ((((Longlndex+NUMROWS) / HUMROWS) < HUMCOLS) tft (LonglndexXHUMROWS < NUMROWS-1)) { /♦ Распределяем 1/4 ошибки на нижний элемент изображения и на элемент справа от нас ♦/ Red[LongIndex+l+HUMROWS] += RedDif/4; Blue[Longlndex*1+HUMROWS] += BlueDif/4; Green[Longlndex+l+HUMROWS] += GreenDif/4; /♦ если значение точки переходит */ if(Red[LongIndex+l+HUMROWS] > COLLEVELS-1 II Green[Longlndex+l+HUMROWS] > COLLEVELS-1 11 Blue[LongIndex+l+HUMROWS] > COLLEVELS-1) /* не обрабатываем дальше этот элемент ♦/ Red[LongIndex+l+HUMROWS] — RedDif/4; Blue[Longlndex+l+HUMROWS] - BlueDif/4; Green[Longlndex*1+HUMROWS] -= GreenDif/4; } 1 1 } 1 /* Данная функция проводит частичную нормализацию значений элементов изображения. Для увеличения яркости значения цветовых регистров масштабируются. ♦/
166 Глава 5. Программные средства видеопреобразователя Листинг 5.4. (Продолжение) void ScaleColRegisters(void) { unsigned Index; unsigned MaxVal » 0; unsigned Tenp; /* Поиск максимального значения компонентов цвета в формате RGB. */ for(lndexa0; Index < MAXNUMCOLREGS;Index++) { if (SColRegs[Index] .Red > MaxVal) MaxVal a SColRegs[Index].Red; if (SColRegs [Index] .Green> MaxVal) MaxVal a SColRegs[Index] .Green; if (SColRegs [Index] .Blue> MaxVal) MaxVal » SColRegs[Index].Blue; /* Масштабирование всех компонентов цветовых регистров. */ for(Index; Index < MAXNUMCOLREGS;Index++) { /* Переменная Tenp используется для того, чтобы избежать переполнения переменной типа BYTE. ♦/ Tenp » SColRegs[Index] .Red*(unsigned) MAXCOLREGVAL; Tenp /a MaxVal; SColRegs[Index] .Red s Tenp; Tenp a SColRegs[Index] .Green*(unsigned) MAXCOLREGVAL; Tenp /a MaxVal; SColRegs[Index] .Green» Tenp; Tenp « SColRegs[Index] .Blue*(unsigned) MAXCOLREGVAL; Tenp /» MaxVal; SColRegs [Index] . Blue» Tenp; void InstallPalette256( void ) { register unsigned Index; /♦ Для каждого установленного графического режима мы должны загрузить цвета в ЦАП. В режиме VGA с 256 цветов палитра не используется. ♦/ for (Index; Index < MAXNUMCOLREGS; Index++) { /♦ установка цветового регистра ♦/ regs.h.ah » 0x10; regs.h.al » 0x10; regs.x.bx » Index;
Программа вывода оцифрованного изображения с полным набором цветов 167 regs.h.dh = SColRegs[Index].Red; regs. h. ch = SColRegs [Index] • Green; regs.h. cl = SColRegs [Index] .Blue; int86(VIDEO,ftregs,ftregs); 1 /♦ Данная функция создает из изображения выполнимый файл с расширением .СОМ для вывода изображения на экран. Она записывает в заданный файл небольшой сегмент кода, за которым следуют 256 значений цветовых регистров и данные оцифрованного изображения. */ void VriteComFile(char «Filename) < FILE «OutPutFile; char String[80]; unsigned Index, PixelValue, Col, Row; BYTE FileCode □ « <0xB4,OxOF,OxCD,0x10,0xA2,0x37,0x01,0xB4,0x00,OxBO,0x13,OxCD,0x10, 0xB8,0x12,0x10,OxBB,0x00,0x00,0xB9,0x00,0x01,OxBA,0x38,0x01,OxCD, 0x10,0xB9,0x00,OxFA,OxBE,0x38,0x04,0xB8,0x00,OxAO,0x8E,OxCO,OxBF, 0x00,0x00,0xF3,0xA4,0xB4,0x00,OxCD,0x16,0xB4,0x00,OxAO,0x37,0x01, OxCD,0x10,0xC3,0x00}; if (!strchr(Filename, ’. ’)) /* имеет ли файл расширение 7 ♦/ < strcpy(String,Filename); /♦ копируем имя файла в буфер */ Filename = String; /* Filename указывает на этот буфер */ strcat (Filename, ".сот") ; /♦ добавляем расширение .сот при необходимости */ } /♦ открываем выходной файл ♦/ if ((OutPutFile = fopen(Filename,"wb")) == NULL) < printf("Cannot open Image .COM file\n"); exit(l); } /♦ записываем в файл сегмент кода */ for(Index=0;Index < sizeof(FileCode);Index++) if (fputc (FileCode [Index] , OutPutFile) ! = FileCode [Index] ) < restorecrtmodeO; DeAllocMemory(); printf("Error writing Image.COM code seg\n"); exit(l); } /♦ записываем значения цветовых регистров ♦/ for(Index«0;Index < MAX256PALETTECOLORS;Index++) < if(fputc(SColRegs[Index]•Red,OutPutFile) != SColRegs[Index].Red)
168 Глава 5, Программные средства видеопреобразователя Листинг 5.4. (Продолжение) { restorecrtmode(); DeAllocMemory(); printf("Error writing Image.COM red color reg\n"); exit(l); if (f putc (SColRegs [Index] . Green, Out Put File) ! = SColRegs [Index] . Green) restorecrtmode(); DeAllocMemory(); printf("Error writing Image.COM green color reg\n"); exit(l); if(fputc(SColRegs[Index].Blue,OutPutFile) •= SColRegs[Index].Blue) restorecrtmode(); DeAllocMemory(); printf("Error writing Image.COM blue color reg\n"); exit(l); /* записываем в файл данные изображения */ for(Row=0; Row < NUMROWS; Row++) for(Col=0; Col < NUMCOLS; Col++) Pixel Value = GetPixel256(Col,Row); /♦ значение считывается с дисплея */ f putс(PixelValue,OutPutFile); fclose(OutPutFile); } /* Данная функция выправляет соотношение геометрических размеров изображения. В действительности видеопреобразователь оцифровывает изображение размером 320x240, которое затем сжимается в буфер 320x200. Подробное описание см. в гл. 12. ♦/ void Aspect Correct (unsigned Correct Aspect, char huge «Inlmage, char huge *0utImage) register unsigned Col, Row; register unsigned LowerBufferRow,UpperBufferRow; register unsigned Upper Intensity, Lower Intensity; register BYTE Intensity; unsigned long InlmageBuf Off set, OutlmageBuf Offset; double FractionalRowAddr, RowDelta; if(CorrectAspect) < Message("Correcting Aspect Ratio of Image\n"); /* для каждой колонки выходного буфера (всего 320 колонок) */
Программа вывода оцифрованного изображения с полным набором цветов 169 for(Col»0; Col < LRMAXCOLS; Col++) /♦ Определяем начало информации о данной колонке в видеобуфере. ♦/ InlmageBufOffset = (long) SOURCEIMAGEROWS ♦ Col; OutlnageBufOffset = (long) LRMAXROWS ♦ Col; /* для каждой строки выходного буфера ♦/ for(Row=0; Row < LRMAXROWS; Row++) /♦ Определение действительного номера оцифрованной строки (из 240). Вычисленный адрес будет располагаться между двумя действительными адресами и поэтому будет дробным. ♦/ FractionalRowAddr = ROWASPECTCORRECTION * (double) Row; /* Получение адресов байтовых строк непосредственно до и после рассчитанного дробного адреса. ♦/ Lower Buffer Row = (unsigned) FractionalRowAddr; UpperBufferRow » LowerBufferRow + 1; Lower Intensity я InInage [InlmageBufOffset + LowerBufferRow]; Upper Intensity я InInage [InlnageBuf Offset + UpperBuff erRow] ; /♦ Определения разности между дробным адресом и более младшим действительным адресом. Эта разность используется в процессе интерполяции. ♦/ RowDelta = FractionalRowAddr - LowerBufferRow; /♦ Использование интерполяции для определения интенсивности точек в данной строке. ♦/ Intensity я RowDelta* ((double) Upper Intensity - Lowerlntensity) + Lower Intensity; /♦ Запись полученной интенсивности в выходной буфер изображения. ♦/ Out Inage [Out InageBuf Of fset+Row] = Intensity; /♦ Данная функция выводит на экран подсказку в случае ошибки оператора. После вывода подсказки выполнение программы прерывается. ♦/ void ShowHelp( void ) printf ("\nThis progran digitize and displays a color inage.It\n"); printf("is envoked as follows:\n\n"); printf("Usage: cvideo [-a -d -h -0 -p -s -v -x] filename <cr>\n");
170 Глава 5. Программные средства видеопреобразователя Листинг 5.4. (Продолжение) printf(" -a correct aspect ratio of image\n"); printf(" -d use Floyd-Steinberg dithering\n"); printf(" -h or ? show help\n"); printf(" -o calculates optinun color paletteW); printf(" -p create PCX output file\n”); printf(" —s scales color register values to nax\n"); printf(" “V displays progran progress inf ornation\n"); printf(" -x create executable display program\n"); printf (" filenane is nane given to generated display f ile(s) .\n"); printf(" Do not specify a file extension, it will be provided.\n\n"); exit(l); 1 /* основная функция программы */ void nain(short argc, char *argv[]) < unsigned FileNaneCounter, Argindex; char «InageFileNarie; InitGraphics(); clrscrO; printf("Digitize, Display and Save a Color Inage\n\n"); /♦ Установка параметров по умолчанию. ♦/ FloydStein » FALSE; /♦ Псевдотонирование отсутствует ♦/ OptinunColors • FALSE; Verbose » FALSE; /♦ не будьте многословны */ GenConFile » FALSE; /♦ не создавайте .СОМ-файл ♦/ GenPCXFile » FALSE; /♦ не создавайте .РСХ-файл ♦/ ScaleColRegs » FALSE; /* не масштабируйте значения цветовых регистров ♦/ CorrectAspectRatio » FALSE; /♦ не исправляйте пространственные соотношения ♦/ /♦ обработка командной строки ♦/ FileNaneCounter = 0; /♦ счетчик имен файлов, указанных пользователем ♦/ for(Arglndexsl; Argindex < argc; Arglndex++) if(*argv[Argindex] != >->) /♦ если символ не является ♦/ { /♦ должно быть имя файла ♦/ if (*argv[Argindex] »»>?>) /♦ требуется ли подсказка ♦/ ShowHelpO; if (FileNaneCounter >1) /♦ разрешено только одно имя файла ♦/ ShowHelpO; /♦ если имен больше - следует вывести подсказку*/ InageFileNane » argv[Argindex]; /♦ сохранение имени файла изображения ♦/ FileNaneCountег++; /♦ увеличение значения счетчика для определения ошибки ♦/ } else { /♦ символ является ключом командной строки ♦/ switch(*(argv[Argindex]+1)) /♦ обработка ключа ♦/ {
Программа вывода оцифрованного изображения с полным набором цветов 171 case ’а’: case ’А’: CorrectAspectRatio = TRUE; break; case ’d’: case ’D4 FloydStein « TRUE; break; case ’h’: case ’H’: ShowHelpO; break; case ’o’: case ’O’: OptimumColors = TRUE; break; case ’p’: case ’₽’: GenPCXFile « TRUE; break; case ’s’: case ’S’: ScaleColRegs = TRUE; break; case ’v’: case ’V’: Verbose я TRUE; break; case ’x’: case ’X’: GenComFile = TRUE; break; default: printf ("Error “ invalid end line switch encountered\n"); ShowHelpO; 1 1 1 if ((GenComFile | GenPCXFile) kA (FileNameCounter != 1)) printf("Error - single filename required for output file(s)\n"); ShowHelpO; 1 /♦ размещаем всю необходимую память ♦/ if (! AllocMemory О ) printf ("memory allocation error - program terminated !\n"); exit (ENoMemory); /* Создание структуры данных, в которой содержится информация о режиме работы видеопреобразователя. Эта структура передается видеопреобразователю во время вызова функции InitializeDigitizer. ♦/
172 Глава 5. Программные средства, видеопреобразователя Листинг 5.4. (Продолжение) Req.ComputerType = PS220; Req.PrtBase « LPT1; Req.HMode = LovRes; Req. VMode = NonInterlace; Req.NumberOfPasses я 1; Req.Flags = OL; Req.FirstLine = 0; Req.FirstPixel = 0; Req.LastPixel = 320; /* Если требуется коррекция геометрических размеров изображения» следует оцифровать изображение размером 320x240. */ if (CorrectAspectRatio) Req. Last Line = 240; else Req.LastLine = 200; InitializeDigitizer(AReq) ; /* передача структуры данных видеопреобразователю ♦/ printf ("Put Red filter in front of the camera\n"); printf ("Press a key when readyXn"); getch (); Message ("Acquiring the Red Picture Data from the Digit izer\n"); if(CorrectAspectRatio) Req.PictBuf = ImageBuf; else Req.PictBuf = Red; GetPictureO; Aspect Correct (Correct AspectRatio, ImageBuf, Red) ; printf ("Put Green filter in front of the earnera\n") ; printf ("Press a key when readyXn"); getchO; Message ("Acquiring the Green Picture Data from the Digit izerXn"); if(CorrectAspectRatio) Req.PictBuf я ImageBuf; else Req.PictBuf e Green; GetPictureO; AspectCorrect(CorrectAspectRatio,ImageBuf,Green); printf("Put Blue filter in front of the cameraXn"); printf("Press a key when readyXn"); getchO; Message ("Acquiring the Blue Picture Data from the Digit izerXn"); if (Correct AspectRatio) Req.PictBuf я ImageBuf; else Req.PictBuf = Blue; GetPictureO; AspectCorrect (CorrectAspectRatio, ImageBuf ,Blue) ;
Программа, вывода оцифрованного изображения с полным набором цветов 173 Message (’’Picture data acquired. Processing the palette data ...\n"); Message("Normalizing color data\n"); Normalize(Red); Normalize(Blue); Normalize(Green); Message("Building color frequency table\n"); ScanColorFrequencies(); Message("Selecting optimum color palette\n"); SelectColorBoxesO; SortColors(); /* вывод на экран итогового изображения с соответствующей палитрой */ Set256ColorMode(); DisplayImageDataO; if (ScaleColRegs) ScaleColRegistersO; Install256Palette(); if (GenPCXFile) /* создаем PCX-файл для вывода изображения на дисплей */ WritePCXFile(ImageFileName,8,320,200,1,320); if (GenComFile) /♦ создаем исполнительный файл для вывода цветного изображения на дисплей ♦/ WriteComFile(ImageFileName); /* окончание программы */ get ch (); restorerectmodeO; DeAllocMemory (); closegraph(); Создание цветных изображений не является тривиальной задачей. Эта про- грамма является одной из самых сложных представленных в книге. Для полу- чения и вывода на дисплей цветного изображения требуется свыше 6000 строк на языке С и 400 строк кода ассемблера. Хотя получаемые результаты довольно эффектны, они могут быть существенно улучшены за счет устранения опреде- ленных ограничений, которые обсуждаются далее. Общий принцип работы программы Цветные изображения получаются путем трех независимых оцифровок изобра- жения с использованием трех различных цветных светофильтров (между видео-
174 Глава 5. Программные средства видеопреобразователя камерой и объектом). В качестве цветов фильтров выбираются три основных цвета — красный, зеленый и синий. Из трех различных оцифровок программа может определить для каждого элемента изображения те цветовые составля- ющие, которые наилучшим образом передают цвет данного элемента. Будучи загруженными в конце концов в один из 256 цветовых регистров в графическом адаптере VGA, эти составляющие создадут цвет, очень близкий к цвету исход- ного изображения в том месте, которое представлено на экране данным элемен- том изображения. Это предельно упрощенное объяснение будет развито ниже в оставшейся части главы. Очевидно, что объект, изображение которого необходимо оцифровать, дол- жен оставаться неподвижным во время всех трех стадий процесса оцифровки. Если в процессе оцифровки объект будет подвижным, «база данных» изображе- ния (красный, зеленый и синий буферы данных изображения) будет искажена, что приведет к выводу недостаточно совершенного цветного изображения. Ино- гда, однако, движение может быть использовано в процессе оцифровки для со- здания особого эффекта, что подробно описано ниже (раздел «Видоизменения, манипуляции и экспериментирование»). Экспериментально установлено, что система зрения человека (система глаз- мозг) способна различать приблизительно 350000 цветов. Любая природная кар- тина, которую мы наблюдаем, состоит из многих цветов, как резких, так и слабо выраженных. Система глаз-мозг использует преобладающие цвета для переда- чи информации об изображении в наш мозг. Более тонкие составляющие цве- та в воспринимаемом изображении создают большее количество подробностей изображения, но не являются необходимыми для его распознавания. Кроме то- го, мозг сам дополняет подробности, которые, как ему представляется, были потеряны. Тогда как человеческий глаз способен различать большое количество цветов, компьютер и видеомониторы не способны передавать многие уникальные цвета сколько-нибудь удовлетворительно. Как мы видели, наибольшее число одновре- менно выводимых цветов, доступных для системы, оснащенной VGA, составля- ет 256 — менее одной тысячной от того количества, которое различает глаз. Однако при оптимальном выборе 256 преобладающих цветов, составляющих ре- альную картину, можно получить точное оцифрованное цветное изображение. Более того, как ни странно, даже использование для вывода изображения всего 16 определенных цветов дает довольно хорошие результаты. Однако использова- ние для вывода на дисплей изображения менее 16 цветов существенно ухудшает соответствие изображения действительности. В последней демонстрационной программе мы будем использовать режим 13Н адаптера VGA исключительно для получения цветных изображений с разреше- нием 320 х 200 в 256 цветах. Это оказывается приемлемым компромиссом между разрешением, числом цветов и объемом использованной памяти. Лучше иметь, конечно, более высокое разрешение и большее количество цветов, но, как мы увидим ниже в разделе «Компромиссы», это не обходится даром. Мы оставля- ем читателю возможность расширить демонстрационную программу, чтобы она соответствовала его личным потребностям. Большинство больших программ, таких сложных, как эта демонстрационная программа, предусматривают возможности для приспособления работы програм-
Программа вывода оцифрованного изображения с полным набором цветов 175 мы к требованиям пользователя. Данная программа не является исключением. Она расширяет интерфейс пользователя (командную строку) по сравнению с предыдущей демонстрационной программой, чтобы обеспечить оператору мно- го возможностей управления работой программы. Работа программы цветной оцифровки cvideo.exe станет понятной после изучения возможностей командной строки. В табл. 5.2 показано, какие возможности существуют, и приведено их описание. Любое число различных ключей командной строки может быть использовано для совместного изменения конечного изображения. Каждый ключ должен быть отделен от предыдущего пробелом. Ключи могут быть набраны как прописными, так и строчными буквами. Можно также запустить программу без каких-либо ключей или имен файлов. Это приведет к тому, что цветное изображение будет получено и выведено на дисплей без какой-либо дополнительной обработки и выходные файлы создаваться не будут. Процесс приема исходных данных изображения в трех цветах от видеопре- образователя и преобразования их в формат, удобный для вывода на графиче- ский адаптер VGA, можно разбить на четыре стадии. Первая стадия — дис- кретизация изображения для определения распределения цветов. В результате этой стадии получаем трехмерную цветовую гистограмму (RGBCube в нашем исполнении), используемую на второй стадии. Вторая стадия — выбор карты цветов. Здесь из входного распределения цве- тов (или гистограммы), полученного на первой стадии, извлекаются оптималь- ные 256 цветов, требуемые для вывода на дисплей оцифрованного цветного изо- бражения. Это распределение цветов представляет собой содержание исходного изображения, как оно воспринято видеопреобразователем. Являясь 6-разрядным устройством, видеопреобразователь присваивает значение от 0 до 63 (2 в степени 6) каждому элементу изображения. Поскольку оцифровка цветного изображе- ния происходит в результате трех независимых стадий, общее число возможных цветов составляет 2 в степени 18 (64 х 64 х 64 или 262144). Поэтому из возможных 262144 цветов, воспринимаемых видеопреобразователем, выбираются оптималь- ные 256 цветов. Опыт показывает, что обычно при оцифровке цветных фотогра- фий видеопреобразователь воспринимает от 3000 до 5000 уникальных цветов. Таблица 5.2. Ключи командной строки программы CVIDEO Использование программы: cvideo [-a -d -h. -о -р -s -v -х][имя файла]. Элементы в квадратных скобках являются необязательными. - а Этот ключ позволяет изменять масштаб изображения, получаемого видео- преобразователем, в двух измерениях. Введение ключа существенно замедляет работу программы cvideo. В деталях изменение масштаба описано в гл. 12. - d Введение этого ключа вызывает выполнение псевдотонирования по методу Флойда-Стейнберга. Эта процедура стремится исправить ошибки, появляющиеся при выборе цвета, не точно соответствующего действительности, путем распреде- ления ошибки между соседними элементами изображения. - h Этот ключ вызывает вывод на дисплей окна краткой подсказки, описыва- ющей работу программы cvideo и каждого из ключей командной строки. После вывода окна подсказки программа cvideo немедленно прекращает работу.
176 Глава 5. Программные средства видеопреобразователя Таблица 5.2. (Продолжение) - о Этот ключ сообщает программе о том, что. для каждого элемента изображе- ния необходимо искать цвет оптимального соответствия вместо метода «наилуч- шей оценки», используемого по умолчанию. Использование ключа увеличивает время обработки изображения примерно от 30 с до 20 мин. Ценность дополни- тельного качества изображения, получаемого при этом, не очевидна. - р Введение этого ключа приводит к созданию PCX-файла изображения для изображения, выведенного на дисплей VGA. Когда ключ введен в командную строку, требуется имя файла без расширения. Имя созданного PCX-файла изо- бражения будет filename.pcx. - s Этот ключ вызывает изменение масштаба вычисленных значений цветовых регистров в сторону увеличения до максимального значения. Отношение цвето- вых составляющих для каждого цветового регистра остается неизменным. При этом увеличивается яркость изображения, что обычно делает его более привле- кательным. - v Этот ключ устанавливает флаг Verbose. Это приводит к тому, что програм- ма выдает сообщения о своей работе. В режиме Verbose все стадии, необходимые для получения и обработки изображения перед выводом, будут отображаться на дисплее. - х Этот ключ вызывает создание СОМ-файла, который будет выводить цвет- ное изображение на дисплей в процессе выполнения программы. Данная особен- ность называется «автовывод» и подробно рассмотрена в главе ниже. Когда ключ введен в командную строку, требуется имя файла без расширения. Имя програм- мы автовывода будет filename.сот. Существует много различных методик и алгоритмов, описанных в литерату- ре, для выполнения трехмерной оптимизации, необходимой при выборе карты цветов. Некоторые из них лучше работают с одним типом изображения, чем с другим. Алгоритм, используемый в этой программе, называется алгоритмом срединного сечения. Этот алгоритм был разработан Паулем Хекбертом и опу- бликован в ACM Computer Graphics Journal в 1982 г. (см. раздел «Литература»). Данный алгоритм работает хорошо независимо от типа оцифровываемого изо- бражения. Код для алгоритма срединного сечения на языке Си написан Дэном Баттерфилдом. Его код был переведен на Turbo С и встроен в среду видеопре- образователя автором. Дэн любезно разрешил использовать его код в этой книге. На третьей стадии устанавливается соответствие между всеми цветами, со- ставляющими исходное изображение, и наилучшим образом передающими их цветами в карте цветов. Установленное соответствие обычно хранится в таблице квантования (RGBCube) для ускорения процесса на стадии 4. Другими словами, происходит установление соответствия между тысячами цветов в исходном изо- бражении и 256 цветами, которые будут использованы для вывода изображения на дисплей. После того как выбрана карта цветов на стадии 2 и создана таблица кванто- вания на стадии 3, четвертой стадией процесса будет квантование и вывод изо- бражения на дисплей. На этой стадии каждый элемент исходного изображения получает соответствующий цветовой регистр с помощью таблицы квантования и затем записывается непосредственно в память дисплея VGA. Конечная стадия процесса вывода на дисплей — установка правильной 256-цветовой палитры.
Программа вывода оцифрованного изображения с полным набором цветов 177 Рис. 5.2. Технические характеристики диска цветных светофильтров. Примечания 1 Фильтр может быть круглым или квадратным. 2 Диаметр центрального отверстия #8. Цветные светофильтры Единственным дополнительным оборудованием для получения цветных изобра- жений (в дополнение к оборудованию, необходимому для работы предыдущих демонстрационных программ) являются цветные светофильтры. Требуются три различных цветных светофильтра — красный, зеленый и синий. Эти фильтры можно ставить между видеокамерой и оцифровываемым объектом вручную, но более удобным является их расположение в виде цветного диска, установленно- го на копировальном стенде, как показано на рис. 5.1. Чтобы устройство было универсальным, в цветной диск может быть введен прозрачный сектор. Он уста- навливается перед камерой, когда оцифровываются монохромные изображения. Как можно ожидать, цветовые характеристики цветных светофильтров суще- ственно влияют на качество получаемых оцифрованных цветных изображений. В идеале цветные светофильтры должны иметь ту же цветовую насыщенность, что и основные цвета видеомонитора, на который будут выводиться цветные изобра- жения. Под цветовой насыщенностью подразумевается преобладающая длина 12-3
178 Глава 5. Программные средства видеопреобразователя волны и чистота цвета. К сожалению, цветовые технические характеристики для мониторов компьютеров трудно найти. Для точного выбора требуемых красно- го, синего и зеленого светофильтров можно использовать технические характе- ристики монитора, если они имеются и выражены в терминах цветовых техни- ческих условий CIE (Международной Комиссии по Освещению). Вместо точных технических характеристик монитора для выбора фильтров мы использовали экспериментальный метод. Технические характеристики для диска светофиль- тров, полученные в результате, показаны на рис. 5.2. Этот диск светофильтров был испытан с двумя различными компьютерными мониторами (цветным мони- тором Amiga RGB и монитором IBM 8512) и были получены хорошие результаты. Компромиссы Программы, применяемые на практике, всегда требуют компромиссов (а ино- гда даже и не столь практичные демонстрационные программы). Компромиссы, введенные в демонстрационную программу, связаны, во-первых, с объемом па- мяти, адресуемым прикладной программой под управлением DOS, и во-вторых, с максимально доступным для 256 цветов разрешением дисплея VGA. Алгоритм, лежащий в основе демонстрационной программы, не имеет каких- либо присущих ему ограничений. Однако эти ограничения должны быть учтены в его работе, чтобы он мог функционировать на базе обычно имеющихся в рас- поряжении аппаратных и программных средств. Самым большим препятствием, которое следует преодолеть при попытке расширить возможности этой демон- страционной программы, является предел памяти DOS в 640К байт. Структуры данных, используемые программой, требуют больших объемов па- мяти. Любые попытки уменьшить объем требуемой памяти сильно повлияли бы на качество работы программы. Большие структуры данных и объемы памяти, требуемые для каждой из них, обобщены ниже в таблице. Эти числа отражают компромиссы, необходимые для того, чтобы позволить данной демонстрационной программе работать под управлением DOS. Структура данных Имя Коли- чество Размер в байтах Общий размер Блок Box 256 16 4096 Сортированный блок SBox 256 16 4096 Регистры цвета ColRegs 256 3 768 Сортированные регистры цвета SColRegs 256 3 768 Буферы изображения Red, Green, Blue RGBCube 3 64000 192000 Трехмерный массив цветов 1 65536 32 х 32 х 32 х 2 65536 Приблизительный общий необходимый объем памяти составляет 267264 байт. Чтобы иметь работающую прикладную программу, необходим дополнитель- ный объем памяти помимо той, которая используется структурами данных де- монстрационной программы. Дополнительная память требуется самой системе
Программа вывода оцифрованного изображения с полным набором цветов 179 DOS, библиотеке времен счета Turbo С и коду демонстрационной программы. Когда вся эта память имеется в распоряжении, демонстрационная программа «cvideo» имеет достаточно памяти для успешной работы в полностью оснащен- ной системе. Можно сэкономить лишь очень малый объем памяти. Первый компромисс, сделанный в демонстрационной программе (чтобы по** зволить ей работать в среде DOS) — выделенный размер трехмерного массива цветов RGBCube. В идеале каждый размер куба должен равняться числовому значению наибольшего измеренного значения элемента изображения плюс один, или 64. Массив RGBCube должен иметь размер 64 х 64 х 64 х 2, или 524288 байт. Как можно видеть, если даже один RGBCube требует более 0.5М байт памяти, программа не сможет работать в среде DOS. Чтобы сделать размер массива RGBCube меньше оптимального, нужно изме- нить масштаб данных элементов изображения, поступающих от видеопреобразо- вателя, и нормировать их, чтобы правильно расположить данные в массиве ча- стоты цветов RGBCube. Эта дополнительная стадия обработки не требовалась бы, если бы был доступен больший объем памяти. Изменение масштаба удаляет приблизительно один бит динамического диапазона изображения, который слабо влияет на качество получаемых изображений. Нехватка доступной памяти влияет также на разрешение обрабатываемых изображений. Трем буферам изображения 320 х 200 требуется 192000 байт па- мяти. Если увеличить разрешение изображения до 640 х 480, общий объем па- мяти, необходимый буферам изображения, возрастет до 921600 байт. Конечно, стандартный графический адаптер VGA фирмы IBM в любом случае не мог бы вывести изображение 640 х 480 с 256 цветами, но графический адаптер VGA не- зависимых поставщиков смог бы и соответствие действительности здесь было бы лучше: изображения 640 х 480 с 256 цветами выглядят как цветные фотографии. Лучшим способом решения проблем с ограничениями памяти, налагаемыми операционной системой DOS, было бы избавиться от DOS. Можно создать вер- сию данной демонстрационной программы, которая будет работать под управле- нием одной из доступных в настоящее время операционных систем, работающих в защищенном режиме, таких как OS/2 или Unix, для которых не существует ограничений памяти. Карта графического адаптера VGA независимых постав- щиков (или карта и монитор IBM 8514/А) была бы последним шагом к выводу на дисплей цветного изображения высокого разрешения. Получение цветного изображения Стадии, которые проходит демонстрационная программа при получении и об- работке изображений с полным набором цветов, лучше всего иллюстрируются псевдокодом, представленным ниже. Видно, что на практике требуется гораздо больше стадий, чем было описано в теоретическом обсуждении, представленном выше. Номера стадий, приведенные в скобках, соответствуют номерам стадий, данным в теоретическом обсуждении. Функция получения и вывода на дисплей цветных изображений Begin Инициализировать графическую подсистему VGA Разобрать все аргументы командной строки. Соответственно установить глобальные переменные. 12*
180 Глава 5. Программные средства видеопреобразователя Отвести память для всех структур данных программы и буферов изображения. Построить структуру ImageReq для получения изображений с разрешением 320x200. Инициализ1фовать видеопреобразователь для изображений 320x200. Получить изображение с разрешением 320x200 через красный фильтр. Исправить пропорции изображения по требования. Получить изображение с разрешением 320x200 через зеленый фильтр. Исправить пропорции изображения по требовании. Получить изображение с разрешением 320x200 через синий фильтр. Исправить пропорции изображения по требовании. Нормализовать красные, зеленые и синие данные изображения. Построить таблицу частоты цветов (RGBCube) для изображения (Первая стадия). Выбрать карту цветов (цветовые блоки) (Вторая стадия). Рассортировать цвета по уровни яркости. Выбрать режим VGA 13Н для вывода изображения иа дисплей. Вывести изображение иа дисплей (Третья и четвертая стадии). Установить палитру VGA из карты цветов (Продолжение четвертой стадии) • Изменить масштаб значений регистров палитры по требовании. Создать COM-файл, программу автовывода по требовании. Создать PCX-файл по требовании. Подождать, пока пользователь нажмет клавишу. Покинуть графический режим и возвратиться в текстовой режим. Освободить вси память, занимаемую программой. Закрыть графическую библиотеку. End Описание механики алгоритма срединного сечения лучше всего оставить его автору Паулю Хекберту. Подробности см. в его статье, приведенной в разделе «Литература», а также в комментариях к листингу 5.4. Выходные файлы изображения Эта программа может создавать два очень различных выходных файла. Пер- вый — PCX-файл изображения с расширенной структурой палитры будет созда- ваться для изображения, выведенного на дисплей, если при запуске программы использован ключ командной строки -р. Для вывода на дисплей цветного изо- бражения формата PCX в любой момент может быть использована программа view.exe, представленная в конце гл. 6. Должное объяснение формата файла PCX также дано в гл. 6. Второй тип выходного файла — так называемый файл автовывода (Auto- Display). Файл автовывода создается тогда, когда использован ключ командной строки -х. Это особый тип файла, который содержит малый участок кода, 256 значений регистров цвета и данные растра для цветного изображения 320 х 200. Файл создается в формате COM-файла системы DOS и получает имя, определен- ное пользователем. Тот факт, что этот файл является COM-файлом, означает, что файл запускается непосредственно путем набора его имени после пригла- шения DOS или если его имя включено в пакетный командный файл (.ВАТ). Файл автовывода, созданный данной программой, при его запуске сразу выведет содержащееся в нем изображение с полным набором цветов. При последующем
Программа вывода оцифрованного изображения с полным набором цветов 181 кратком обсуждении обращайтесь к коду программы автовывода, представлен- ному в листинге 5.5. Кодовая часть программы автовывода состоит из 23 строк кода ассемблера. Основная часть файла состоит из данных цветовых регистров и данных растра изображения. Все COM-файлы автовывода имеют размер 64824 байт, поскольку данные растра не сжаты. Действие этого кода лучше всего можно понять при изучении псевдокода, представленного ниже. Функция автовывода Begin Получить текущий видеорежим путем запроса BIOS. Записать текущий видеорежим в память. Установить видеорежим в режим 13Н 320x200 с 256 цветами. Скопировать значения RGB для всех 256 регистров цвета из файла в регистры цвета VGA. Установить регистр СХ на число байтов растровых данных, хранящихся в файле, который будет перенесен в видеопамять VGA. Установить регистры DS:SI, указывающие на растровые данные в файле. Установить регистры ES:DI, указывающие на видеопамять VGA, расположенную по адресу А000:0000. Переместить блок растровых данных в видеопамять. Подождать, пока пользователь нажмет клавишу (клавиатура опрашивается BIOS). Получить из памяти исходный видеорежим. Восстановить исходный видеорежим. End Функция этого кода должна быть очевидной и не требует дальнейшего объ- яснения. Файл автовывода создается функцией WriteComFile демонстрационной про- граммы. В этой функции объектный код, создаваемый при ассемблировании про- граммы автовывода, записывается в массив FileCode. Последовательность дей- ствий, необходимых для создания выполнимой программы автовывода, не явля- ется неожиданной. Сначала открывается выходной файл в двоичном режиме с именем, определенным пользователем в командной строке, и расширением СОМ, дописанным программой. Затем выполняемый код записывается в файл из мас- сива FileCode. После этого в файл записываются данные цветовых регистров. Записываются составляющие RGB первого регистра цвета, затем второго и т.д. В конце непосредственно с дисплея VGA с помощью функции GetPixel256 счи- тываются данные растра изображения и записываются в файл. Данные запи- сываются в нормальном построчном растровом формате от верха дисплея вниз. После записи данных растра файл автовывода закрывается и процесс его созда- ния завершается. Листинг 5.5. Программа автовывода Далее следует содержимое файла iMagecoM.asn: : Программа вывода цветного изображения 320x200 {Примечание: эта программа работает только с адаптером VGA
182 Глава 5, Программные средства видеопреобразователя Листинг 5.5. (Продолжение) ; Написана Крейгом А. Линдли ; Последние изменения внесены: 06/21/89 ;Эта программа создает файл .СОМ, который при выполнении выводит ;на экран цветное изображение размером 320x200 256 цветов, полученное ;с помощью видеопреобразователя VIDEO KEYBOARD EQU EQU 10H 16H ;код видеопрерывания BIOS ;код прерывания клавиатуры BIOS IMAGESIZE EQU 64000 ; размер изображения 320x200 в байтах RGBSIZE EQU 256*3 ; количество байтов в формате RGB GETVIDEOMODE EQU OFH ;код функции BIOS SETVIDEOMODE EQU OOH ;код функции BIOS SET256C0L0RM0DE EQU 13H ;режим 320x200 256 цветов SETCOLREGBLOCK EQU 1012H ;код функции/подпрохраимы BIOS VGAMEMSEG EQU OAOOH ; сегмент видеопамяти VGA GETKEY EQU OOH ;код функции ожидания нажатия клавиши CSEG SEGMENT PARA PUBLIC ’CODE’ ASSUME CS:CSEG,SS:CSEG,DS:CSEG,ES:CSEG устанавливается загрузчиком ORG 100H ;положение начала файла .COM ;Примечание: во время преобразования адресов, приведенных в этом файле, ;в процедуре WriteComFile не забудьте переменить порядок байтов адреса, ;иначе итоговый .СОМ файл не будет выполняться ; Начало программы вывода изображения Start proc near mov ah,GETVIDEOMODE определение текущего видеорежима int VIDEO ;результат в регистре al mov VMode, al ; сохраняем видеорежии mov ah,SETVIDEOMODE устанавливаем код функции mov al,SET256C0L0RM0DE ; специально для адаптера VGA int VIDEO устанавливаем видеорежим mov ax, SETCOLREFBLOCK ; подготовка к загрузке цветовых регистров mov bx,0 ; начинаем с регистра 0 mov ex,256 ;все 256 регистров mov dx,offset ColorRegs ;указывает на rgb[256] ; предполагаем es=cs int VIDEO ; загружаем регистры BOV ex,IMAGESIZE ; количество перемещаемых байтов BOV si,offset iBageData ; источник данных ; предполагаем ds~cs BOV ax,VGAMEMSEG ;данные передаются в видеопамять VGA BOV es,ax ; начальный адрес а А000:0000
Программа вывода оцифрованного изображения с полным набором цветов 183 MOV di,0 гер Bovsb ; перемещаем данные MOV ah,GETKEY ; ожидание нажатия клавиши int KEYBOARD ; запрашиваем BIOS mov ah,SETVIDEOMODE ; устанавливаем код функции BOV al, VMode ; исходный видеорежим int VIDEO ; устанавливаем видеорежим ret ;возврат в DOS Start endp ; Данные программы также записываются в сегмент кода св VMode DB 0 ;для хранения исходного видеорежима ColorRegs DB RGBSIZE DUP(O) ;256 триад RGB ImageData DB IMAGESIZE DUP(O) ;пространство для размещения 64000 байт ;данных изображения CSEG ENDS END Start Никаких других методик, требующих обсуждения, в этом коде не предста- влено. Перенос данных в видеопамять VGA осуществляется по той же методи- ке, которая представлена в гл. 1 при обсуждении функций ассемблера VGA, содержащихся в файле vgagraph.asm. Видоизменения, манипуляции и экспериментирование Имея действующие демонстрационные программы, вы можете начинать экспери- ментировать. Сначала вам, возможно, захочется оцифровать фотографии вашей семьи и друзей; затем, пейзажи и т. д. Чем более точные оцифрованные изобра- жения вы будете получать на первом этапе экспериментирования, тем лучше. Используйте различные ключи командной строки для изменения работы про- граммы, наблюдайте производимые эффекты. Ключ изменения степени яркости -s обычно дает яркое, приятное для просмотра изображение. После того как первоначальная новизна исчезнет, вы можете попытаться сде- лать много других экспериментов, чтобы добиться особых эффектов на ваших оцифрованных изображениях. Не все пройдет успешно, но если вы не будете пробовать, то не будете и знать. В конце концов, вы сразу будете видеть ре- зультаты — вам не нужно будет ждать пока проявится пленка, как если бы вы пользовались традиционными пленкой и камерой. Например, попробуйте оци- фровать изображение, используя другие светофильтры. Попытайтесь двигать светофильтры во время оцифровки изображения. Если вы имеете доступ к фото- графическим светофильтрам для особых эффектов, можете поместить их перед видеокамерой для получения того же эффекта на вашем оцифрованном изобра- жении. Оцифровывайте изображение через призму или калейдоскоп или через
184 Глава 5. Программные средства, видеопреобразователя розовые стекла. Попробуйте слегка двигать оцифровываемую картинку между сменой фильтров. Возможно, вы добьетесь трехмерного эффекта, если вы сме- стите красную и зеленую оцифровку и наденете специальные очки, чтобы по- смотреть на конечное изображение. Другого интересного эффекта можно достичь с помощью изменения кода в демонстрационной программе для уменьшения числа цветовых регистров, ис- пользуемых для вывода изображения на дисплей. Для этого замените число в операторе #define MAXNUMCOLREGS 256 на меньшее значение (например 16) и заново откомпилируйте демонстрационную программу. Отметьте, как такое из- менение влияет на качество изображения. Попробуйте подвигать изображение в 16 цветах. Возможно, вы захотите добавить к демонстрационной программе код, пери- одически сдвигающий значения цветовых регистров. Это может дать изображе- ние, которое как будто хочет «выпрыгнуть» на вас с экрана. Вы также можете изменить масштаб регистров цвета на протяжении короткого периода времени, чтобы ваше изображение при выводе на дисплей приобрело полную яркость, а затем вернулось к исходной яркости. При получении особых эффектов изо- бражения ничего не запрещено. Экспериментируйте, сколько хотите. Возможно, ваши цветные изображения с особыми эффектами можно будет продавать как предметы искусства. Заключение В этой главе рассмотрено, как написанный на языке Си код высокого уровня используется для управления работой видеопреобразователя. Мы также обсу- дили возможности улучшения качества оцифрованных изображений. Наконец, мы показали, как можно получить, вывести на дисплей и записать монохромные и цветные изображения в виде PCX-файлов или программы автовывода. В этой книге содержится множество примеров изображений, полученных с помощью видеопреобразователя. Мы получили эти изображения с помощью программных средств, идентичных представленным в этой главе. Мы заканчиваем обсуждение аппаратных и программных средств видеопре- образователя. Предполагается, что ваш видеопреобразователь работает и вы по- нимаете, как можно видоизменить код управления видеопреобразователя высо- кого уровня для ваших конкретных требований. Я приветствую тех, кто собрал свой собственный видеопреобразователь. Надеюсь, это не был болезненный опыт. Я действительно надеюсь, что это была радость. Мы перейдем теперь к обсуждению изображений и основ их обработки. Не бу- дем предполагать, что все изображения, с которыми будем иметь дело, созданы нашим видеопреобразователем. Вместо этого допустим, что мы можем обрабаты- вать любое изображение, имеющее формат стандартного графического файла. В гл. 6 подробно описаны два наиболее важных формата графических файлов — PCX и TIFF.
Глава 6 Форматы и функции графических файлов В этой главе рассмотрены следующие вопросы: • Различие между векторной и растровой графикой. • Стандартные форматы графических файлов. • Организация графических файлов PCX. • Необходимые элементы для чтения и записи РСХ-файлов. • Организация графических файлов TIFF. • Что необходимо для чтения и записи TIFF-файлов. • Вывод программы «view.exe» PCX- и TIFF-файлов на дисплей. Введение Сами по себе оцифрованные изображения имеют ограниченную ценность. Они становятся полезными, когда находятся в такой форме, что ими могут манипу- лировать другие прикладные программы. Для этого необходимо привести дан- ные изображения в «стандартную форму», которую могут интерпретировать и использовать другие прикладные программы. Если изображение может быть введено в рисующую программу или программу электронной верстки, его мож- но затем ввести в презентации, просмотры слайдов, отчеты, публикации и т. д. Изображение может быть также использовано для производства на заказ тен- нисных маек. Все коды обработки изображений в ч. II книги манипулируют изо- бражениями, хранящимися в стандартном формате РСХ-файлов. В общем случае графические программы можно разделить по способу хра- нения и вывода на дисплей данных изображения на две категории: растровый формат и векторный формат. Растровый формат состоит из ряда элементов изо- бражения, покрывающих всю площадь дисплея. Растровые изображения обыч- но получаются с помощью электронного луча, периодически движущегося по поверхности изображения, и заранее установленного приемника (например, ви- деокамеры). Элементы, составляющие растровое изображение, не обязательно имеют какое-либо отношение друг к другу. Растровому изображению не прису- ще понятие формы. Растровые изображения часто используются для презента- тивной графики, где важны соображения художественности и качество изобра- жения. Основные преимущества растрового формата состоят в следующем: а) Простота вывода данных из растрового устройства ввода (например, ви- деопреобразователя или сканера) на растровое устройство вывода (напри- мер, монитор компьютера или графический принтер).
186 Глава 6. Форматы и функции графических файлов б) Вывод на дисплей растровых данных обычно происходит быстрее, чем вы- вод векторных данных, поскольку этот вывод осуществляется растровыми устройствами, и, следовательно, не требуется производить преобразования из векторной в растровую форму. Векторный формат предполагает использование для создания изображений отрезков направленных линий вместо элементов изображения. Векторное изо- бражение составлено из форм, состоящих из отрезков прямых. Основой векторных изображений являются связанность и иерархия. Для дан- ных изображения в векторном формате легко определить, какие составляющие отрезки прямых образуют тот или иной объект. Векторные изображения исполь- зуются прежде всего для приложений САПР, где важны точный масштаб и вза- имосвязь элементов. Примером программы, использующей векторную графику, является AutoCAD. Другой важной областью применения векторных изображе- ний является создание карт и работа с ними. Взаимосвязь объектов на карте (например, всех ливнестоков в городе) и возможность видеть только избранные объекты или группы объектов часто очень полезны. Векторные изображения легко выводятся на плоттер, поскольку плоттер является устройством векторного типа. Векторные изображения также легко могут быть преобразованы в растровый формат и/или печатную копию с помо- щью принтера. К сожалению, преобразование из растровой в векторную форму осуществить гораздо сложнее. В этой главе мы рассмотрим два основных растровых формата — формат РСХ/РСС, поддерживаемый программой PC PaintBrush фирмы ZSoft, и фор- мат TIFF, поддерживаемый программами фирм Aldus, Microsoft, Hewlett-Packard и многими прикладными программами электронной верстки. Векторный фор- мат не будем рассматривать из-за его ограниченной применимости в приклад- ных программах обработки изображений. Оба формата графических файлов — РСХ/РСС и TIFF мы обсудим достаточно подробно. Представим код на языке Си для чтения и записи файлов в этих форматах. Он представлен в виде библиоте- ки функций, легко присоединяемой к другим прикладным программам. Обсудим программу «view», способную выводить на дисплей правильно форматированные изображения любого формата. Программа «view» является примером того, как прикладная программа может использовать графическую библиотеку. Формат графических файлов РСХ/РСС Этот формат — одна из самых ранних попыток в мире PC обеспечить возмож- ность хранения и стандартизации графических изображений. Стандартный фор- мат файлов был необходим, чтобы дать возможность разным прикладным про- граммам работать с полученным изображением и обеспечить сжатие файлов для экономии места на диске. Формат графических файлов РСХ/РСС является при- мером метода, используемого на практике и ставшего стандартным по молча- ливому согласию. Поскольку он был широко распространен долгое время, фор- мат графических файлов РСХ/РСС, вероятно, поддерживается большим коли- чеством графических прикладных программ, чем все остальные РС-совместимые форматы графических файлов, вместе взятые.
Формат графических файлов РСХ/РСС 187 О Главный информационный заголовок Значения 16-цветовой палитры Заголовок файла РСХ/РСС - 128 байт 127 128 Дополнительный информационный заголовок Данные изображения, закодированные по алгоритму RLE Размер определяется изображением. Изображения с высоким разрешением занимают больше места Необязательная расширенная цветовая палитра 769 байт. 1 байт флага и 768 байт данных в формате RGB Рис. 6.1. Структура файла РСХ/РСС. Прежде чем продолжить обсуждение, сделаем одно предостережение. Код, представленный ниже, предполагает наличие дисплея VGA, поскольку дисплеи CGA и EGA и, следовательно, файлы, созданные на этих дисплеях, не поддержи- ваются. В последующем обсуждении мы коснемся различия в кодировке файлов CGA и EGA и файлов VGA, но мы предоставляем читателю возможность само- му внести изменения в код, чтобы он мог поддерживать эти графические ада- птеры. В результате PCX-изображения, созданные для графических адаптеров, отличных от VGA, не будут правильно выводиться на дисплей с помощью име- ющейся библиотеки функций PCX. Причины станут очевидными, когда будут обсуждаться палитры PCX. Формат графических файлов РСХ/РСС не является достаточно гибким по отношению к содержащейся в нем информации. Формат файла жесткий, имею- щий заголовок файла постоянной длины, за которым следуют данные растрового изображения, а затем может находиться расширенная структура палитры. На
188 Глава 6. Форматы и функции графических файлов рис. 6.1 показана компоновка типичного файла РСХ/РСС. Как будет видно из дальнейшего, файл РСС является подмножеством (или контуром) файла PCX. Благодаря простоте этого формата файла код, требуемый для поддержки PCX, также прост для понимания, изменения и использования. Библиотека функций кода РСХ/РСС содержится в двух файлах — «pcx.h» и «рсх.с». Оба файла показаны в виде листинга 6.1. При последующем обсуждении обращай- тесь к этому листингу. Вся информация о структуре и формате файлов РСХ/РСС была взята из документации, поставленной корпорацией ZSoft к программе PC PaintBrush. Листинг 6.1. Функции библиотеки PCX Ниже приводится содержимое файла pcx.h: /****************************************/ /* Header PCX-файл для */ /♦ функции библиотеки формата PCX */ /♦ разработан в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 12/08/89 ♦/ /дедедедедедедедедедедедедедедедедедедеде/ «define MAXSCREENWIDTH 640 «define MAXSCREENHEIGHT 480 «define BITSPERBYTE 8 «define MAXPLANES 4 «define MAXBYTESPERSCAN 320 «define MAXPALETTECOLORS 16 «define MAX256PALETTECOLORS 256 /♦ Управляющие определения. Они управляют функцией DisplayImagelnBuf. ♦/ «define INITVGALOADPALETTE TRUE «define NOVGAINIT FALSE «define VAITFORKEY TRUE «define NOWAITFORKEY FALSE /♦ Определение битовых флагов ошибок. ♦/ «ifndef _CompletionCode_ «define .CompletionCode. typedef int CompletionCode; «endif /♦ Определения битовых флагов ошибок для функций PCX. ♦/
Формат графических файлов РСХ/РСС 189 ♦define NoError О ♦ define EBadParams -1 ♦ define EFileNotFound -*2 ♦ define EReadFileHdr -3 ♦ define ENotPCXFile -4 ♦ define ECorrupt -*5 ♦ define EVrtFileHdr -6 ♦ define EWrtOutFile -7 ♦ define EWrtScanLine -8 ♦ define EPCCFile -9 ♦ define EGraphics rlO ♦ define ENoMemory -11 ♦ define EVrtExtPal -12 /♦ Определения битовых флагов ошибок для Misc ♦/ ♦ define EKernelSize -21 /♦ Структуры данных и определения для PCX-файл а ♦/ ♦define PCXHdrTag 10 ♦define MaxRepCount 63 ♦define PCX256ColorTag 12 /♦ метка PCX-файл a ♦/ /♦ максимальное количество повторяющихся байтов*/ /♦ метка расширенной палитры в PCX-файле ♦/ /♦ Примечание: компилятор языка Си долкен генерировать побайтно выровненный код для структуры ColorRegister, чтобы она занимала точно 3 байт. Если компилятор генерирует пословно выравненный код, эта структура будет иметь в длину 4 байт, что нарушит функционирование программы. ♦/ typedef struct { BYTE Red; BYTE Green; BYTE Blue; } ColorRegister; struct PCXFileHeader { BYTE Header; BYTE Version; /♦ компоненты цвета в формате RGB ♦/ /♦ регистр ♦/ BYTE Encode; BYTE BitPerPix; unsigned XI; unsigned Yl; unsigned X2; unsigned Y2; unsigned Hres; unsigned Vres; }; /♦ метка PCX-файла ♦/ /♦ 0 = версия 2.5 ♦/ /♦2 s версия 2.8 с палитрой ♦/ /♦3 s версия 2.8 или 3.0 без палитры ♦/ /♦5 s версия 3.0 с палитрой ♦/ /♦ тип кодирования ♦/ /♦ количество битов на элемент изобракения ♦/ /♦ размеры изобракения ♦/ /♦ разрешение графического адаптера по горизонтали ♦/ /♦ разрешение графического адаптера по вертикали ♦/
190 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) struct PCXInfo BYTE Vnode; /♦ этот байт всегда должен быть нулевым */ BYTE NunOfPlanes; /♦ количество битовых плоскостей */ unsigned BytesPerLine; /* количество байтов на строку ♦/ BYTE unused [60] ; /♦ дополняет заголовок до 128 байт */ >; struct PCX.File struct PCXFileHeader PCXHeader; ColorRegister Palette[MAXPALETTECOLORS]; /♦ не более 48 байт ♦/ struct PCXInfo Info; /* Структура данных расширенной палитры для PCX-файлов с 256 цветами */ struct ExtendedPalette BYTE ExtendedPalette; ColorRegister Palette[MAXPALETTECOLORS]; /♦ не более 768 байт ♦/ /* Прототипы функций ♦/ void DisplayPCXFile(char *FileName, int Verbose); /* вывод PCX-файла на экран */ void VritePCXFile(char *FileName,unsigned BitsPerPixel, unsigned MaxX, unsigned MaxY, unsigned Planes, unsigned BytesPerLine); unsigned InstallPCXFilePalette(void); /* загрузка палитры из PCX-файла ♦/ CoupletionCode ReadPCXFileToBuf(char *FileName, BYTE huge * * Buf f erPtr); CoupletionCode Writ ePC XFileFromBuf(char *FileName, BYTE huge *ImageMemory); void DisplayInagelnBuf(BYTE huge *Inage, unsigned SetMode, unsigned Pause); CoupletionCode ReadRawImageFileToBuf(char *FileName, unsigned InageVidth, unsigned InageHeight, BYTE huge ♦ *BufferPtr); ConpletionCode WriteRawImageFilFromBuf(char *FileName, unsigned InageVidth, unsigned InageHeight, unsigned Transpose, BYTE huge ♦ *BufferPtr); Далее следует содержание файла рсх.с:
Формат графических файлов РСХ/РСС 191 /*««««««««««««*««««««««****«««««««*««****/ /* Библиотека функций */ /♦ поддержки графического формата */ /♦ РСХ-изображения ♦/ /♦ Разработана в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли */ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 11/21/89 */ /г***************************************/ «include <stdio.h> «include <string.h> «include <process.h> «include <conio.h> «include <dos.h> «include <alloc.h> «include <graphics.h> «include "misc.h" «include "pcx.h" «include "vga.h" /* Внешние глобальные переменные ♦/ struct PCX_File PcxData; /* заголовок РСХ-файла ♦/ unsigned Imagetfidth, ImageHeight; /* Переменные, глобальные только для данного файла */ static FILE «PCXFile; /* идентификатор файла */ static BYTE ScanLine[MAXBYTESPERSCAN]; static BYTE PixelColorMumDfAXSCREENVIDTH]; static unsigned Is256ColorFile; static struct ExtendedPalette Color256Palette; /♦ Запуск функций ♦/ CompletionCode ReadPCXFileHdr(char «Filename, int Verbose); unsigned Index; char String [80]; Is256ColorFile ° FALSE; /* инициализируем переменные режима работы*/ if (!strchr(Filename,1. ’)) /* имеет ли файл расширение ? */ strcpy(String,Filename); /* копируем имя файла в буфер */ Filename = String; /* Filename указывает на этот буфер */ strcat (Filename, ".рсх") ; /* добавляем расширение ".рсх" в случае необходимости */ 1 /* открываем PCX-файл */ if ((PCXFile « f open (Filename, "rb")) == NULL) {
192 Глава 6. Форматы я функции графических файлов Листинг 6.1. (Продолжение) printf("PCX file: %s not found\n" ,FileMame); return(EFileHotFound); 1 /* считываем заголовок файла */ if(fread(&PCXData,sizeof(struct PCX.File),l,PCX_File) !« 1) { printf("Error reading PCX file header\n"); return (EReadFileHdr); /* проверяем метку PCX-файла */ if(PCXData.PCXHeader.Header !- PCXHdrTag) printf ("Error not a RCX file\n"); return(EMotPCXFile); /♦ PCX-файл успешно открыт. Выводим на экран информация) о нем в случае необходимости */ if(Verbose) { clrscrO; printf ("PCX Image Information for file: %s\n\n",Filename) ; printf ("\t Version: %d\n", PCXDat a. PCXHeader. Vers ion) ; printf ("\tCompression: %s\n", PCXData.PCXHeader.Encode «« 0 ? "Hone":"RLL"); printf("\tBits Per Pixel: %d\n",PCXData.PCXHeader.BitPerPix); printf("\tXl: 7.d\n",PCXData.PCXHeader.XI); printf("\tYl: %d\n",PCXData.PCXHeader.Yl); printf("\tX2: %d\n",PCXData.PCXHeader.X2); printf("\tY2: Xd\n",PCXData.PCXHeader.Y2); printf("\tHoriz Resolution: 7.d\n",PCXData.PCXHeader.Hres); printf("\tVert Resolution: %d\n",PCXData.PCXHeader.Vres); printf ("\tVMode: %d\n",PCXData. Info.Vmode) ; printf ("\tMumber of Planes: %d\n",PCXData.Info.MumOfPlanes) ; printf ("\tBytes Per Scan Line One Plane :%d\n", PCXData. Inf o. BytesP er Line) ; printf ("\nHit any key to proceed\n"); getchO; /* ждем действия оператора */ clrscrO; printf("Color Register Values for PCX file: %s\n\n",FileMame); for (Index • 0; Index < MAXPALETTECOLORS; Index++) printf ("Palette Index: 7.2d R » 7.2x G 7.2x В %2x\n", Index,PCXData. Palette [Index] . Red, PCXData. Palette [Index] . Green, PCXDat a.Palett e[Index].Blue); printf ("\nHit <Enter> to proceed - *C to abort\n");
Формат графических файлов РСХ/РСС 193 get char (); /* «дем действия оператора */ } return(NoError); static CoupletionCode ExpandScanLine ( FILE *InFile) { register short ВitNun; register unsigned ByteNun; register short CharRead; unsigned InPtr,RepCount,PixelsData; unsigned BytesToRead, PlaneNun, Shift Count; unsigned ByteOffset, BitOffset; BytesToRead » PCXDat a. Info. NunOf Planes * PCXData. Inf o. But es Per Line; InPtr » Shift Count » 0; /* инициализируем переменные */ do { CharRead » getc (InFile); /♦ считываем байт из файла ♦/ if (CharRead s= EOF) /* проверяем на наличие метки EOF ♦/ return (FALSE) ; /♦ прерываем программу */ if ((CharRead & OxCO) ««OxCO) /* повторение метки ? */ RepCount » CharRead ft "OxCO; /* считаем повторения */ CharRead « getc(InFile) ; /* считываем повторяющийся байт ♦/ if (CharRead »« EOF) /♦ проверяем на наличие метки EOF */ return (FALSE); /* прерываем программу */ while (RepCount—) /* раскрываем повторяющийся байт ♦/ ScanLine [InPtr++] » CharRead; else /♦ считан байт данных */ ScanLine[InPtr++] » CharRead; /♦ записываем его в буфер */ } while(InPtr < BytesToRead); /♦ раскрываем строку изображения ♦/ /♦ Мы получили массив ScanLine, который состоит из секций NunOfPlanes по BytesPerLine байт длиной .каждая. Для обычных изображений EGA/VGA такой массив содержит 4 плоскости по 80 байт. Для изображения VGA 256 цветов одна строка содержит одну плоскость длиной 320 байт. Для нормального изображения нужно преобразовать битовые плоскости в массив PixelColorNun для вывода изображения на экран. Каждый элемент такого массива соответствует точке не экране монитора. Для изображений с 256 цветами преобразование не требуется и мы должны просто скопировать ScanLine в PixelColorNun, поскольку в этом случае нет перекрывающихся битовых плоскостей. ♦/ if (PCXData.PCXHeader.X2 »» 319) /♦ если изображение содержит 256 цветов */ nencpy (PixelColorNun, ScanLine, InageVidth); 13-3
194 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) else /* обычный файл ♦/ { /♦ обнуляем массив PixelColorNun ♦/ nenset(PixelColorNun, ’\0’,InageWidth); for(PlaneNun«O; PlaneNun < PCXData.Info.NunOfPlanes; PlaneNun-ь-»-) { ByteOffset “ PlaneNun ♦ PCXData.Info.BytesPerLine; for(ByteNun~0; ByteNun < PCXData.Info.BytesPerLine;ByteNun++) { /♦ считываем 8 бит данных для одной плоскости ♦/ PixelsData “ ScanLine[ByteOffset+ByteNun]; BitOffset « ByteNun ♦ BITSPERBYTE; for(BitNun»BITSPERBYTE-l; Bitnun >« 0; BitNun—) if(PixelsData ft (1 « BitNun)) { /♦ логически комбинируем компоненты цвета оператором OR ♦/ PixelColorNun[ВitOffset + (7 - BitNun)] I» (1 « Shift Count) ; ShiftCount++; /♦ Массив PixelColorNun создан и содержит значение цвета для каждой точки дисплея. Остается возвратить значение, указывающее на правильное выполнение этой работы. ♦/ return(TRUE); unsigned InstallPCXFilePalette(void) { struct palettetype palette; union REGS regs; unsigned Index; /* Загружаем палитру из файла изображения во всех случаях, за исключением Version = 3. В последнем случае используем стандартную палитру. ♦/ if (PCXData.PCXHeader.Version !» 3) if (Is256ColorFile) /* если режим VGA13H ♦/ {
Формат графических файлов РСХ/РСС 195 /♦ Если мы находимся в этом месте, мы работаем с файлом, созданным в режиме 13Н. В этом режиме внутренняя палитра адаптера VGA игнорируется. В цветовые регистры загружаются цвета из расширенной палитры файла изображения. Значения цветов в расширеной палитре файла PCX в четыре раза превышают значения адаптера VGA, поэтому все 256 цветных регистров должны быть промасштабированы соответствующим образом. ♦/ for(lndex=0; Index < MAX256PALETTEC0L0RS; Index++) { Color256Palette. Palette [Index] .Red »s 2; Color256Palette. Palette [Index] .Green »= 2; Color256Palette. Palette [Index] .Blue »« 2; 1 /♦ установка блока цветовых регистрбв ♦/ regs.h.ah ж 0x10; regs.h.al я 0x12; regs.x.bx ж 0; regs.x.cx = MAX256PALETTEC0L0RS; _ES ж FP.SEG(&Color256Palette.Palette); regs.x.dx « FP_0FF(ftColor256Palette.Palette); int86(VIDEO,ftregs,ftregs); return(TRUE); /♦ указывает на успешную установку палитры ♦/ 1 else { /♦ Мы имеем 16-цветное изображение. Следует построить палитру из значений, содержащихся в PCX-файле. Палитра устанавливается последовательно и цветовые регистры загружаются информацией из файла изображения. Все значения цвета масштабируются. ♦/ palette.size = MAXPALETTECOLORS; for(Index - 0; Index < MAXPALETTECOLORS; Index++) { palette.colors[Index] s Index; PCXData.Palette [Index] .Red »s 2; PCXData.Palette [Index] . Green »s 2; PCXData.Palette [Index] .Blue »= 2; 1 /♦ установка блока цветовых регистров ♦/ regs.h.ah s 0x10; regs.h.al • 0x12; regs.x.bx = 0; 13*
196 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) regs.x.cx » MAXPALETTECOLORS; _ES = FP_SEG(APCXData.Palette); regs.x.dx = FP_OFF(APCXData.Palette); int86(VIDE0,&regs,ftregs); /* разрешаем использование палитры» которую мы считываем из файла ♦/ setallpalette(Apalette); return (TRUE); /* указывает на успешную установку палитры ♦/ 1 1 else return (FALSE); /* палитра не загружена и информация о ней отсутствует */ 1 /* Данная функция считывает PCX-файл в буфер памяти. Она не модифицирует текущую палитру VGA. ♦/ CompletionCode ReadPCXFileToBuf (char «FileName, BYTE huge * * BufferPtr) { register unsigned ScanNum; /«расширяемая и выводимая на экран строка*/ register unsigned ColNum; /* считываемый элемент изображения */ int PCXError; BYTE huge «ImageMemory; /♦ область памяти» в которую считывается изображение */ unsigned long PixelBuf Off set; if((PCXError = ReadPCXFileHdr(Filename,FALSE)) != NoError) return(PCXError); /* Заголовок файла прочитан. Теперь надо считать данные изображения. */ /* РСС-файлы не загружаются и не выводятся на экран ♦/ if ((PCXData.PCXHeader.XI »« 0) 11 (PCXData. PCXHeader. Y1 0)) { printf(“Error PCC file not PCX file\n“); return (EPCCFile); 1 /♦ Из информации в заголовке РСХ-файла определяем размер буфера для хранения данных изображения. Устанавливаем соответствующим образом глобальные переменные ImageVidth и ImageHeight. ♦/ if (PCXData.PCXHeader.X2 » 319) { ImageVidth - 320; ImageHeight ж 200; 1 else {
Формат графических файлов РСХ/РСС 197 InageVidth « 640; svitch(PCXData.PCXHeader.Y2) case 479: InageHeight я 480; break; case 349: InageHeight я 350; break; case 199: InageHeight я 200; break; /♦ выделяем память для изображения ♦/ InageMenory я (BYTE huge *) farcalloc((long)InageVidth * InageHeight, sizeof(BYTE)); if (InageMenory яя NULL) printf("Error Not enough nenory for PCX buffer\n"); return (ENoMenory); 1 /♦ Распаковываем и записываем данные из РСХ-файла. Каждый раз обрабатывается одна строка. ♦/ for(ScanNun~0; ScanNun < InageHeight; ScanNun++) if (ExpandScanLine(PCXFile) !- TRUE) printf ("Error Scanline corrupt in PCX file\n"); return (ECorrupt); PixelBufOffset я (long) ScanNun * InageVidth; for(ColNun~0; ColNun < InageVidth; ColNun++) InageMenory [PixelBuf Off set+ColNun] PixelColorNun[ColNun]; 1 1 /♦ Пытаемся считать метку расширенной палитры в конце РСХ-файла. Если считывается символ EOF, следовательно файл не содержит расширенной палитры. ♦/ Is256ColorFile я FALSE; /* устанавливаем флаг указания типа файла ♦/ if(fread(ftColor256Palette,sizeof(struct ExtendedPalette),l,PCXFile) я= 1) /♦ Расширенная палитра успешно считана. Проверяем метку. ♦/ if(Color256Palette.ExtendedPalette “ PCX256ColorTag)
198 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) /♦ Метка правильная. ♦/ Is256ColorFile =- TRUE; /♦ Файл полностью считан. Закрываем его и заканчиваем функцию. ♦/ fclose(PCXFile); ♦BufferPtг = ImageMemory; /♦ возвращаем адрес буфера памяти ♦/ return(NoError); } /♦ Данная функция выводит на экран изображение из буфера памяти. Если параметр SetMode равен TRUE» устанавливается графический режим и загружается палитра. Если же SetMode равен FALSE» изображение выводится в текщем режиме. Если установлен параметр Pause» функция ожидает нажатия клавиши перед возвращением в вызывашщув программу. ♦/ void Display Image InBuf (BYTE huge «Image» unsigned SetMode» unsigned Pause) register unsigned ScanNum» PixelNum; unsigned long PixelBufOffset; if(SetMode) InitGraphicsO; /♦ необходима инициализация графической системы ♦/ if (ImageVidth == 320) /♦ 256 цветов ? ♦/ if (SetMode) /* если необходима инициализация графического режима» устанавливаем его и загружаем палитру ♦/ { Set256ColorMode(); InstallPCXFilePaletteO; } for(ScanNum=0; ScanNum < ImageHeight; ScanNum++) for(PixelNum=0; PixelNum < ImageVidth; PixelNum++) PixelBuf Off set « ScanNum; /* для предотвращения переполнения «/ PixelBufOffset ♦« ImageVidth; PixelBuf Off set +ж PixelNum; PutPixel256 (PixelNum» ScanNum» Image [PixelBuf Off set]); } } else if (SetMode) /♦ если необходима инициализация графического режима ♦/ switch(ImageHeight) /♦ определяем режим адаптера VGA ♦/ { case 480: setgraphmode(VGAHI); break;
Формат графических файлов РСХ/РСС 199 case 350: setgraphmode(VGAMED); break; case 200: setgraphmode(VGALO); break; 1 InstallPCXFilePaletteO; for(ScanHum=0; ScanHum < ImageHeight; ScanNum++) for(PixelHum=0; PixelHum < ImageWidth; PixelHum++) { PixelBufOffset = ScanHum; /♦ для предотвращения переполнения */ PixelBuf Off set ♦= Image Width; PixelBufOffset += PixelHum; put pixel (PixelHum, ScanHum, Image [PixelBuf Off set]); 1 1 if (Pause) /♦ если выбран режим паузы */ getchO; 1 /♦ Данная функция считывает и выводит на экран РСХ-файл. ♦/ void DisplayPCXFile(xhar *FileHame, int Verbose) { register unsigned ScanHum; /♦ Нойер строки, которая будет выведена на экран «/ register unsigned ColHum; /♦ Нойер элемента изображения «/ int PCXError; if ((PCXError • ReadPCXFileHdr(FileHame,Verbose)) != HoError) exit(PCXError); /* Заголовок файла прочитан. Теперь надо считать данные изображения. */ /♦ РСС-файлы не загружаются и не выводятся на экран */ if ((PCXData.PCXHeader.XI !» 0)) 11 (PCXData.PCXHeader.Y1 !» 0)) { printf("Error PCC file not PCX file\n"); exit(EPCCFile); 1 InitGraphics(); /* инициируем графическую систему */ /♦ Из информации в заголовке PCX-файла определяем необходимый режим графического адаптера. Если ширина избражения равна 320 точек, выбираем режим 13Н. В противном случае устанавливаем стандартный режим VGA с разрешением по горизонтали 640 точек. ♦/
200 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) if(PCXData.PCXHeader.Х2 == 319) Set256ColorMode(); InageVidth = 320; InageHeight = 200; } else { InageVidth = 640; switch(PCXData.PCXHeader.Y2) { case 479: setgraphnode(VGAHI); InageHeight = 480; break; case 349: setgraphnode(VGAMED); InageHeight = 350; break; case 199: setgraphnode(VGALO); InageHeight ж 200; break; /♦ Распаковываем и выводим на экран РСХ-файл. ♦/ for(ScanNun=0; ScanNun < InageHeight; ScanNun++) if(ExpandScanLine(PCXFile) != TRUE) printf(”Scanline corrupt in PCX file\n”); return(ECorrupt); if (InageVidth == 320) /♦ режим c 256 цветами */ for(ColNum=0; ColNun < InageVidth; ColNun++) PutPixel256 (ColNun, ScanNun, (int)PixelColorNun[ColNun]) ; else /♦ нормальный режим ~ 16 цветов ♦/ for(ColNun=0; ColNun < InageVidth; ColNun++) putpixel (ColNun, ScanNun, (int) PixelColorNun [ColNun]); /♦ Пытаемся считать метку расширенной палитры в конце РСХ-файла. Если считывается символ EOF, следовательно файл не содержит расширенной палитры. ♦/ Is256ColorFile = FALSE; /* устанавливаем флаг указания типа файла */
Формат графических файлов РСХ/РСС 201 if(fread(&Color256Palette,sizeof(struct ExtendedPalette),1,PCXFile) » 1) /♦ Расширенная палитра успешно считана. Проверяем метку. ♦/ if(Color256Palette.ExtendedPalette == PCX256ColorTag) /♦ Метка правильная. ♦/ Is256ColorFile - TRUE; /♦ Файл полностью считан. Закрываем его и заканчиваем функцию. ♦/ /♦ Устанавливаем палитру, прочитанную из файла. ♦/ InstallPCXFilePaletteO; fclose(PCXFile); /♦ Следующие процедуры создают PCX-файл из растрового изображения или буфера памяти и записывают его на диск. ♦/ CoupletionCode WritePCXHdr(char «FileNaue, unsigned BitsPerPixel, unsigned NaxX, unsigned NaxY, unsigned Planes, unsigned BytesPerLine); struct palettetype palette; uns igned Index; union REGS regs; char String [80]; if (!strchr(FileNaue, ’ •’)) /* есть ли расширение имени файла */ { stгсру(String,FileName) ; /♦ копируем имя файла в буфер ♦/ FileName e String; /♦ FileName указывает на буфер ♦/ strcat(FileNane,“.pcx"); /* добавляем расширение .рсх ♦/ } if ((PCXFile » fopen(FileNaBe,"w+b")) »» NULL) restorecrtmodeO; printf (“Could not open output PCX file\n")-; return(EVrtOutFile); } /♦ инициализируем информацию заголовка файла ♦/ PCXData.PCXHeader.Header = PCXHdrTag; PCXData.PCXHeader.Version = 5; PCXData•PCXHeader•Encode = 1; PCXData.PCXHeader.BitPerPix = BitsPerPixel; PCXData.PCXHeader.XI » 0; PCXData.PCXHeader.Y1 » 0; PCXData.PCXHeader.X2 = NaxX-1; PCXData. PCXHeader. Y2 » NaxY-1;
202 Глава 6, Форматы я функции графических файлов Листинг 6.1. (Продолжение) PCXData.PCXHeader.Hres » МахХ; PCXData.PCXHeader.Vres ® MaxY; ImageVidth MaxX; ImageHeight • MaxY; PCXData. Inf o.Vmode » 0; PCXData. Info. MunOfPlanes » Planes; PCXData.Info.BytesPerLine BytesPerLine; /* Инициализация структуры данных палитры в файле PCX. Эта палитра будет записана в файл независимо от количества цветов в изображении. Если изображение содержит 256 цветов, в конце файла будет дополнительно записана расширенная палитра. Значения элементов структуры палитры нужно умножить на 4 перед записью в файл. */ get palette (ftpalette); for(Index; Index < palette.size; Index++) regs.h.ah 0x10; regs.h.al » 0x15; regs.x.bx ® palette.colors[Index]; int86(VIDEO,ftregs,ftregs); PCXData. Palette [Index] . Red regs .h. dh « 2; PCXData. Palette [Index] .Green regs.h. ch « 2; PCXData. Palette [Index] .Blue regs.h. cl <<® 2; /♦ очищаем неиспользуемые данные в конце заголовка файла */ memset (ftPCXData. Inf о .unused, ’\0 ’ ,sizeof (PCXData. Inf о .unused) ) ; /* записываем заголовок в физический файл */ if(fwrite(ftPCXData,sizeof(struct PCX_File),l,PCXFile) !» 1) { restorecrtmodeO; printf ("Error writing PCX file header\n"); return(EVrtFileHdr); return (NoError); static CompletionCode CompressScanLine(FILE *0utFile) { register unsigned 0utPtr,RepCount,RepChar; register unsigned BytesToVrite; BytesToVrite PCXData.Info.NumOfPlanes * PCXData.Info.BytesPerLine; OutPtr ® 0; /* указывает на данные, подвергающиеся сжатию ♦/ do { RepChar ScanLine [0utPtr++]; /♦ выбираем байт для начала сжатия */ RepCount « 1;
Формат графических файлов РСХ/РСС 203 while ((ScanLine [OutP t г] =RepChar) ftft (Repcount < MaxRepCount) ftft (OutPtr < BytesToWrite)) { RepCount++; /♦ подсчитываем все повторения текущего символа ♦/ OutPtr*1**; /♦ изменяем счетчик и подсчитываем сначала ♦/ /♦ найдена повторяющаяся последовательность или если у символов MSB порознь или вместе установлены» что обрабатывается как последовательность повторяющегося счетчика и символов ♦/ if ((RepCount >1) || (Repchar > OxBF)) { RepCount 1= OxCO; /♦ устанавливаем старшие значащие биты ♦/ if (putc (RepCount» Out File) == EOF) /♦ записываем счетчик в файл ♦/ return (FALSE); /♦ при возникновении ошибки возвращаем индикатор ошибки ♦/ if (putc (RepChar» Out File) =e EOF) /♦ записываем символ в файл ♦/ return (FALSE); /♦ при возникновении ошибки возвращаем индикатор ошибки ♦/ } while(OutPtr < BytesToWrite); /♦ повторяем до тех пор» пока все байты в текущей строке не будут сжаты ♦/ return(TRUE); /♦ указывает на успешное выполнение операции ♦/ /♦ Данная функция записывает на диск изображение в формате PCX из буфера памяти. В файл записывается палитра» которая использовалась бы для вывода изображения на экран. Все элементы заголовка файла PCX берутся из изображения. При этом считается» что содержание изображения может измениться, а его базовые параметры не могут. ♦/ CompletionCode WritePCXFileFromBuf(char «FileName» BYTE huge *ImageMemory) register unsigned PlaneNum»BitNum»ByteNum»PData; register unsigned ScanLineNum»PixelNum» Index;; int PCXError; unsigned long PixelBufOffset; union REGS regs; /♦ записываем заголовок и палитру */ if((PCXError = WritePCXHdr(FileName»PCXData.PCXHeader.BitPerPix PCXData.PCXHeader.X2+1,PCXData.PCXHeader.Y2+1, PCXData.Info.NumOf Planes» PCXData.Info.BytesPerLine)) != NoError) return(PCXError); /♦ Мы считываем изображения из буфера построчно. Для изображений 320x200 256 цветов имеется только одна битовая плоскость» поэтому данные из буфера
204 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) можно непосредственно помечать в массив ScanLine для сжатия. Для нормальных VGA-изображений значение цвета необходимо разложить на его компоненты» которые сжимаются отдельно. По сути» один массив длиной 640 байт» соответствующий 640 различным точкам строки» разбивается на четыре массива по 80 байт (при этом все они содержатся в ScanLine []). Затем отдельные компоненты сжимаются. Компоненты записываются на диск в следующем порядке: синяя составляющая цвета: зеленая составляющая цвета; красная составляющая цвета; интенсивность. ♦/ for(ScanLineNum=0; ScanLineNum < ImageHeight; ScanLineNum++) PixelBufOffset = (long) ScanLineNum ♦ ImageWidth; if(PCXData.PCXHeader.X2 == 319) /♦ если изображение имеет 256 цветов ♦/ for(PixelNum»0; PixelNum < ImageWidth; PixelNum++) ScanLine[PixelNum] » ImageMemory[PixelBufOffset + PixelNum]; else /* обычный файл ♦/ /♦ очищаем массив ScanLine ♦/ /♦ это массив из NumOfPlanes ♦ BytesPerLine байт ♦/ memset(ScanLine»’\0’.MAXBYTESPERSCAN); for(PixelNuma0; PixelNum < ImageWidth; PixelNum++) /♦ считываем значение элемента изображения из буфера ♦/ PData = ImageMemory [PixelBuf Off set + PixelNum] ; ByteNum = PixelNum/BITSPERBYTE; /♦ рассчитываем байтовое смещение ♦/ BitNum = 7 - (PixelNum % BITSPERBYTE); /♦ рассчитываем битовое смещение ♦/ for(PlaneNums0; PlaneNum < PCXData.Info.NumOfPlanes; PlaneNum++) if (PData & (l«PlaneNum) /♦ если бит в плоскости равен 1 ♦/ ScanLine [(PlaneNum ♦ PCXData. Inf о. BytesPerLine) + ByteNum] Is (1 «BitNum); if (CompressScanLine (PCXFile) ! = TRUE) /♦ сжатие полной строки ♦/ restorecrtmode(); printf ("Error writing a compressed scan line\n”); return(EWrtScanLine); /♦ Если мы работаем в режиме 13Н» следует записать в файл метку расширенной палитры сразу после данных изображения. ♦/
Формат графических файлов РСХ/РСС 205 if (InageVidth == 320) /♦ изображение в режиме 13Н ? ♦/ { /♦ да ♦/ /♦ Считываем 256 значений цветовых регистров в формате RGB и записываем их в структуру данных Color256Palette перед запись» в файл. Такой прием используется для того, чтобы гарантировать правильность записи расширенной палитры. ♦/ Color256Palette.ExtendedPalette = PCX256ColorTag; /* считываем блок цветовых регистров ♦/ regs.h.ah = 0x10; regs.h.al = 0x17; regs.x.bx = 0; regs.x.ex = MAX256PALETTEC0L0RS; _ES = FP_SEG(&Color256Palette.Palette); regs.x.dx я FP_0FF(ftColor256Palette.Palette); int86(VIDEO,ftregs,ftregs); /♦ Данные палитры перед запись» в файл нужно промасштабировать. ♦/ for(lndex»0; Index < МАХ256РALETTECOLORS; Index++) { Color256Palette.Palette[Index].Red «= 2; Color256Palette. Palette [Index] .Green «= 2; Color256Palette.Palette[Index].Blue <<= 2; /♦ После того как все цветовые регистры прочитаны, можно записать структуру данных расширенной палитры в РСХ-файл. ♦/ if(Fvrite(ftColor256Palette, sizeof(struct ExtendedPalette),l,PCXFile) != 1) { restorecrtnodeO; printf (’’Error writing extended palette structured’’); fclose(PCXFile); /* закрываем РСХ-файл */ farfree((BYTE far ♦) InageMenory); /♦ освобождаем буфер памяти ♦/ return(EVrtExtPal); } /♦ после того, как файл записан на диск, захфываем его */ f close (PCXFile) ; /♦ закрываем РСХ-файл ♦/ farfгее((BYTE far ♦) InageMenory); /♦ освобождаем память ♦/ retum(NoError); } /* Данная функция записывает в файл PCX изображение, выведенное в данный момент на экран монитора. */
206 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) void WritePCXFile(char *FileName, unsigned BitsPerPixel, unsigned MaxX, unsigned MaxY, unsigned Planes, unsigned BytesPerLine) { register unsigned PlaneNum, В it Num, By t eNum, Pdata; register unsigned ScanLineNum, PixelHum; int PCXError; unsigned Index; union REGS regs; /♦ записываем заголовок файла и палитру */ if((PCXError = WritePCXHdr(Filename,BitsPerPixel, MaxX, MaxY, Planes, BytesPerLine)) != NoError) exit(PCXError); /♦ Мы считываем изображение с экрана построчно. Для изображений 320x200 256 цветов имеется только одна битовая плоскость, поэтому данные из буфера можно непосредственно помещать в массив ScanLine для сжатия. Для нормальных VGA-изображений значение цвета необходимо разложить на его компоненты, которые сжимаются отдельно. По сути, один массив длиной 640 байт, соответствующий 640 различным точкам строки, разбивается на четыре массива по 80 байт (при этом все они содержатся в ScanLine □) • Затем отдельные компоненты сжимаются. Компоненты записываются на диск в следующем порядке: синяя составляющая цвета; зеленая составляющая цвета; красная составляющая цвета; интенсивность. */ for(ScanLineNum=0; ScanLineNum < ImageHeight; ScanLineNum++) { if (PCXData.PCXHeader.X2 ®= 319) /* если изображение имеет 256 цветов ♦/ { for(PixelNum=0; PixelNum < ImageVidth; PixelNum++) ScanLine[PixelNum] = GetPixel256(PixelNum,ScanLineNum); else /♦ обычный файл ♦/ { /♦ очищаем массив ScanLine ♦/ /♦ это массив из NumOfPlanes ♦ BytesPerLine байт ♦/ memset (ScanLine, ’\0’,MAXBYTESPERSCAN) ; for(PixelNum=0; PixelNum < ImageVidth; PixelNum++) { PData = getpixel(PixelNum,ScanLineNum); /* считываем значение элемента изображения ♦/ Byte Num = PixelNum/BITSPERBYTE; /* рассчитываем байтовое смещение ♦/
Формат графических файлов РСХ/РСС 207 BitNum » 7 - (PixelNum % BITSPERBYTE); /♦ рассчитываем битовое смещение ♦/ f or(PlaneNumsO; PlaneNum < PCXDat a. Inf о. NumOf Planes ;PlaneNum++) if (PData ft (1 «PlaneNum)) /* если бит в плоскости равен 1 */ ScanLine [(PlaneNum ♦ PCXData. Inf о BytesPerLine) + ByteNum] Is (1 «Bit Num) ; if (CompressScanLine (PCXFile) != TRUE) /♦ сжатие полной строки */ restorecrtmodeO; printf(“Error writing a compressed scan line\n“); exit(EWrtScanLine); } /♦ Если мы работаем в режиме 13Н, следует записать в файл метку расширенной палитры сразу после данных изображения. ♦/ if (ImageVidth == 320) /♦ изображение в режиме 13Н ? ♦/ { /♦ да ♦/ /♦ Считываем 256 значений цветовых регистров в формате RGB и записываем их в структуру данных Color256Palette перед записью в файл. Такой прием используется для того, чтобы гарантировать правильность записи расширенной палитры. ♦/ Color256Palette.ExtendedPalette = PCX256ColorTag; for (Index = 0; Index < MAX256PALETTEC0L0RS;Index++) { regs.h.ah a 0x10; regs.h.al = 0x15 regs.x.bx = Index; int86(VIDEO,ftregs,ftregs); Color256Palette. Palette [Index] .Red = regs.h.dh «= 2; Color256Palette. Palette [Index] . Green » regs. h. ch «= 2; Color256Palette. Palette [Index] .Blue я regs.h.cl «s 2; } /♦ После того как все цветовые регистры прочитаны, можно записать структуру данных расширенной палитры в РСХ-файл. ♦/ if(fwrite(ftColor256Palette, sizeof(struct ExtendedPalette),1,PCXFile) ! = 1) { rest orecrtmode();
208 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) printf ("Error writing extended palette structure\n"); /* после того как файл записан на диск, закрываем его ♦/ fclose(PCXFile); /♦ закрываем PCX-файл ♦/ /♦ Данная функция считывает файл несжатых данных изображения в память. Количество данных определяется размерами изображения. В ответ на возникновение различных ошибок функция возвращает сответствующие коды ошибок. Если считывание завершается успешно, в вызывающую программу передается код NoError и указатель на массив данных изображения. Не делается никаких предположений о формате данных в файле. */ CoupletionCode ReadRawImageFileToBuf (char ♦FileName, unsigned Image Width, unsigned ImageHeight, BYTE huge ♦ ♦BufferPtr) unsigned long RasterSize, Index; BYTE huge ♦ImageBuffer; FILE ♦ImageDataFile; int DataRead; /* Приписываем указателю ошибочное значение до тех пор, пока данные не будут успешно прочитаны. ♦/ ♦BufferPtr » NULL; /♦ Рассчитываем размер буфера, необходимый для хранения изображения. ♦/ RasterSize = (long)ImageVidth ♦ ImageHeight; /♦ Достаточно ли памяти для размещения буфера. ♦/ if(RasterSize >= farcoreleft()) printf("Not enough memory for image !\n"); return (ENoMemory); /♦ Размещаем буфер в глобальной памяти. ♦/ ImageBuffer я (BYTE huge ♦)farcalloc(RasterSize, (unsigned long) sizeof(BYTE)); /♦ Открываем файл данных. ♦/ if((ImageDataFile я fopen (FileName,"rb")) == NULL) { printf("Cannot open image data file:%s\n",FileName); farfree((char far ♦) ImageBuffer); return(EFileNotFound);
Формат графических файлов РСХ/РСС 209 /♦ Считываем RasterSize байт данных из файла. ♦/ for(lndex=0; Index < RasterSize; Index++) { /♦ Проверяем каждую операцию чтения. ♦/ if ((DataRead = fgetс(ImageDataFile)) == EOF) { fclose(ImageDataFile); /♦ закрываем файл ♦/ farfгее( (char far ♦) ImageBuffer); return (ECorrupt); /♦ возвращаем код ошибки ♦/ /♦ Если операция прошла успешно, записываем количество прочитанных байт в массив. ♦/ ImageBuffer[Index] я (BYTE) DataRead; /♦ Закрываем файл, запоминаем указатель и возвращаем код NoError. ♦/ fclose(ImageDataFile); «BufferPtr = ImageBuffer; /« возвращаем указатель на буфер «/ return (NoError); } /♦ Данная функция записывает в файл данные изображения, полученные от видеопреобразователя. Примечание: эта функция преобразует поколонный формат данных видео- преобразователя в построчное представление изображения в том случае, когда параметр Transpose равен TRUE. В противном случае выходной файл будет содержать данные в том порядке, в каком они поступили от видеопреобразователя. ♦/ CompletionCode Writ eRavImageFileFromBuf (char «FileName, unsigned ImageWidth, unsigned ImageHeight, unsigned Transpose, BYTE huge ♦ «BufferPtr) { unsigned long RasterSize, Index, Col, Row; FILE «ImageDataFile; BYTE WriteData; /* Открываем выходной файл. «/ if((ImageDataFile f open (FileName, "wb")) «« NULL) { printf ("Cannot open image data file:%s\n",FileName); return (EWrtOutFile); } if(!Transpose) /« записываем данные в нормальном формате «/ /« Рассчитываем количество записываемых байт. «/ 14-3
210 Глава 6. Форматы и функции графических файлов Листинг 6.1. (Продолжение) RasterSize » (long)ImageWidth « ImageHeight; /♦ Записываем RasterSize байт данных в файл. ♦/ for(lndex=0; Index < RasterSize; Index++) { /* Определяем записываемый байт. */ WriteData = ImageBuf ter [Index] ; /* Проверяем каждую операцию записи. ♦/ if (fputc (WriteData, ImageDataFile) !» WriteData) { fclose (ImageDataFile) ; /* закрываем файл */ return (EWrt Out File); /« возвращаем код ошибки */ 1 1 } else /♦ записываем данные в формате растра */ { for(Row«0; Row < ImageHeight; Row++) for(Col«0; Col < ImageWidth; Col++) { /♦ Определяем записываемый байт. ♦/ WriteData « ImageBuf f er [(Col « ImageHeight) + Row]; /♦ Проверяем каждую операцию записи. */ if (fputc (WriteData9 ImageDataFile) !ж WriteData) { fclose(ImageDataFile) ; /♦ закрываем файл ♦/ return (EWrtOutFile) ; /♦ возвращаем код ошибки */ 1 } } /♦ Файл успешно записан. Закрываем его, освобождаем память, возвращаем код НоЕггог. ♦/ fclose(ImageDataFile);. farf гее ((char far ♦) ImageBuf f er) ; /* освобождаем память */ return(NoError); 1 Заголовок файла РСХ/РСС Наилучший способ описать формат файла РСХ/РСС — дать содержание за- головочной части файла. Заголовок содержит необходимую информацию для правильной интерпретации растровых данных, следующих за заголовком. Без заголовка данные изображений не могут быть расшифрованы. Как здесь пока- зано, заголовок имеет длину 128 байт и состоит из следующих данных, взятых из файла «pcx.h»:
Формат графических файлов РСХ/РСС 211 typedef struct /♦ определение цветового регистра VGA ♦/ < BYTE Red; /* RGB компоненты цветового регистра ♦/ BYTE Green; BYTE Blue; } ColorRegister; /* Заголовок файла РСХ/РСС ♦/ Struct PCXFileHeader < BYTE Header; /* помечает файл как файл PCX ♦/ BYTE Version; /♦ 0 = версия 2.5 ♦/ /* 2 = 2.8 с палитрой ♦/ /♦ 3 = 2.8 или 3.0 без палитры ♦/ /♦ 5 = 3.0 с палитрой ♦/ BYTE Encode; /* Режим кодирования файла */ BYTE BitPerPixel; /♦ Битов на элемент изображения */ unsigned XI; /* Размер изображения */ unsigned Y1; unsigned Х2; unsigned Y2; unsigned Hres; /♦ Горизонтальное разрешение адаптера */ unsigned Vres; /* Вертикальное разрешение адаптера */ }; struct PCXInfo { BYTE Vnode; /♦ Всегда должно быть равно 0 ♦/ BYTE NumOfPlanes; /* Число битовых плоскостей ♦/ unsigned BytesPerLine; /* Число бит на линию */ BYTE unused [60] ; /♦ Дополняет заголовок до 128 байт ♦/ } /♦ Полная структура файла РСХ/РСС */ struct PCX_file < struct PCXFileHeader PCXHeader; ColorRegister Palette [MaxPaletteColors]; /♦ Максимальный размер 48 байт */ struct PCXInfo Info; }; Структура «PCX_File» представляет полную заголовочную часть файла РСХ/РСС. Внутри этой структуры структура «PCXHeader» представляет собой то, что на рис. 6.1 обозначено как «Главная информация заголовка», «пали- тра» — это массив из 16 цветовых регистров, причем каждый регистр содержит по одному байту для информации о красном, зеленом и синем цветах, и структу- ра «Info», содержащая вспомогательную информацию о файле. Обратите внима- ние, как заполнена структура «Info», чтобы полная длина заголовка составляла точно 128 байт. Каждое из отдельных полей внутри заголовка РСХ/РСС играет важную роль в описании содержащегося в файле изображения. Функции некоторых из этих 14*
212 Глава 6. Форматы я функции графических файлов полей заголовка не являются очевидными, поскольку они основываются на исто-* рических перспективах, которые сейчас ускользают от нас. Эти поля также мо- гут использоваться авторами программ, создающих PCX-файлы по их собствен- ному усмотрению. Хуже то, что другие PCX-программы полностью не восприни- мают заголовок файла, предполагая, что все введенные PCX-файлы были пред- варительно записаны их прикладной системой (и потому декодируются по при- чине известного контекста), как будто не существует других программ, создаю- щих PCX-файлы. Это означает, что код, разработанный для чтения РСХ-файлов, должен работать наилучшим образом при декодировании так называемых стан- дартных РСХ-файлов, создаваемых некоторыми прикладными программами. В дополнение, код, написанный для создания РСХ-файлов, должен точно и полно- стью строить заголовок РСХ-файла, чтобы другие прикладные программы могли правильно декодировать конечные файлы изображений. Теперь обсудим содер- жание полей заголовка файлов PCX, как оно используется кодом, представлен- ным в данной книге. Поле «Header» заголовка файла PCX используется для определения того, является ли файл действительным PCX- или РСС-файлом. Значение, считан- ное из графического файла в это поле, должно равняться десятичному числу 10, чтобы файл рассматривался как РСХ/РСС-файл. Большинство программ, читающих РСХ/РСС-файлы, отвергнут файл с неправильным байтом «Header». Поле «Version» дает номер версии PCX, используемый для кодирования со- держащегося в файле изображения. Основные версии следующие: Version Версия PCX 0 версия 2.5 2 версия 2.8 с палитрой 3 версия 2.8 или 3.0 без палитры 5 версия 3.0 с палитрой Код PCX, представленный в этой главе, может читать версию 3.0 с информа- цией или без информации о палитре, но записывает только версию 3.0 с пали- трой. Поле «Encode» определяет, являются ли данные растрового изображения за- кодированными с ограничением длины прохода (RLL — Run Length Limited) или побайтно запакованными. Бели изображение закодировано по RLL-типу, значе- ние этого поля будет ненулевым. В противном случае, если это значение равно нулю, данные изображения побайтно запакованы. Для всех практических це- лей каждый РСХ/РСС-файл RLL-закодирован для экономии места при записи изображения. Подробное описание RLL-кодирования приведено ниже в разделе «Сжатие с ограничением длины прохода». Поле «BitPerPix» определяет, сколько бит разрядной матрицы используется в изображении на один элемент. Для изображений VGA с 16 цветами и четырьмя битовыми плоскостями значение этого поля равно 1. В этом случае родственному полю «NumOfPlanes» будет приписано значение 4. В изображениях с 256 цветами (режим VGA 13 hex) «BitPerPix» будет равно 8, a «NumOfPlane» равно 1. Поля «XI», «Y1», «Х2» и «Y2» описывают размеры растрового изображения, содержащегося в PCX-файле. Для РСХ-файлов «XI» и «Y1» всегда равны ну-
Формат графических файлов РСХ/РСС 213 лю, а значения «Х2» и «Y2» равны максимальным значениям X- и Y-элементов изображения, доступным дисплею в данном режиме вывода. Другими словами, PCX-файлы заполняют весь экран. С другой стороны, РСС-файлы имеют значе- ния «XI» и «Y1», большие или равные нулю, а значения «Х2» и «Y2», меньшие или равные максимально допустимым значениям дисплея. РСС-изображения — это частичные изображения, которые не обязательно занимают полный экран. РСС-изображения не полностью поддерживаются библиотекой функций PCX, поскольку с помощью значений, содержащихся в полях «XI», «Y1», «Х2» и «Y2», не всегда можно сообщить, какое разрешение должен использовать экран VGA для вывода изображения в часть экрана. Правильная интерпретация двух полей «Hres» и «Vres», описанных ниже, устранила бы эту проблему, если бы эти поля правильно поддерживались всеми прикладными программами РСХ/РСС. К сожалению, это не так. Например, одна версия PC PaintBrush. всегда записы- вает в «Х2» и «Y2» значение 75hex независимо от режима, в котором находится дисплей. Эти несвязанные значения не помогают определить правильный режим дисплея, требуемый для вывода содержащегося в файле РСС-изображения. Поля «Hres» и «Vies» должны всегда содержать разрешение дисплея, требу- емое для вывода растрового изображения. В библиотеке функций PCX эти поля поддерживаются правильно для всех записываемых PCX-изображений. Когда PCX-файл считывается с помощью библиотеки функций PCX, эти поля пропус- каются. Последним мы обсудим поле заголовка PCX «BytesPerLine». Это значение равно числу несжатых байтов данных, необходимых для размещения полной строки развертки (ряда данных с экрана) данных изображения. Для изобра- жений с полем «BitPerPix», равным 1, которые имеют в ширину 640 элементов (изображения 640 х 200 и 640 х 480), «BytesPerLine» равно 80 (640 элементов/8 элементов на байт). Для изображений 320 х 200 (256 цветов) с «BitPerPix», рав- ным 8, «BytesPerLine» равно 320 (320 элементов/1 элемент на байт). Палитры файлов PCX Информация о палитре хранится в PCX-файле, поэтому прикладная програм- ма, читающая данные PCX-изображения, может выводить его на дисплей в тех цветах, в которых оно было создано и должно выводиться. Без информации о палитре пришлось бы выводить изображение в палитре, заданной по умолчанию данной прикладной программой. Цвета в этой палитре могут не соответствовать цветам исходного изображения. Фактически вывод изображения в палитре, за- данной по умолчанию, мог бы полностью испортить изображение. Представьте себе изображение океана, выведенное на дисплей, с водой оранжевого цвета — не очень привлекательная картина. Хранение информации о палитре вместе с РСХ- файлом позволяет не только программе, создавшей изображение, но и другим прикладным программам правильно и точно выводить изображение на дисплей. Важно понять различие между информацией о палитре, хранящейся в РСХ- файле, и механизмом палитры, используемым адаптером VGA. В гл. 1 говорится, что палитра — это не более чем набор цветов, которые могут быть одновремен- но выведены на дисплей и использованы для вывода изображения. Информация о палитре в PCX-файле сообщает правильные цветовые компоненты (сколько
214 Глава 6. Форматы и функции графических файлов процентов красного, зеленого и синего составляют требуемый цвет) для каждо- го цвета, который требуется для вывода изображения. Для каждого цвета, ис- пользуемого в растровом изображении, должна быть одна компонента палитры в PCX-файле. Порядок компонент палитры в PCX-файле определяет значение элементов изображения, требуемое для вывода необходимого цвета. Например, элемент изображения со значением 0 должен быть выведен в цвете, определен- ном нулевой компонентой палитры (т. е. первой по счету компонентой палитры). Элемент изображения со значением 15 будет выведен в цвете, определенном ком- понентой палитры с номером 15, и т. д. Для правильного вывода PCX-изображения необходимо построить палитру для графического адаптера VGA и загрузить цветовые регистры VGA соответ- ствующими RGB-компонентами необходимых цветов. Простейшим путем выпол- нения этой задачи является загрузка цветовых регистров, начиная с регистра О, RGB-компонентами, считанными из РСХ-файла, а затем создание палитры VGA из 16 компонент, содержащей значения от 0 до 15. Это обеспечит необходи- мое соотношение между значениями элементов изображения и цветов. Заметим, однако, что это только одна из многих возможных процедур соотнесения, ко- торые дают тот же результат. Изображение может быть выведено на дисплей в правильных цветах (в тех, в которых предполагалось его выводить) только в том случае, когда палитра цветов, используемая графическим адаптером VGA, совпадает с палитрой, содержащейся в РСХ-файле. В PCX-файлах используются два различных механизма палитры. Исходный механизм палитры встроен в заголовок файла. Эта палитра применяется для изображений, содержащих до 16 цветов. Структура палитры имеет участок па- мяти для 16 значений цветовых регистров, содержащих по одному байту инфор- мации о красном, зеленом и синем цветах. Поэтому общий объем, отводимый в заголовке РСХ-файла для этой структуры палитры, составляет 48 байт (16 значений цветовых регистров хЗ байт каждый). Интерпретация информации о палитре зависит от используемого графического адаптера. Для нормальных изображений VGA в 16 цветах первые 16 цветовых реги- стров заполняются цветами, описанными их RGB-компонентами в структуре па- литры РСХ-файла. Реальная палитра, которая, как вы помните, представляет собой всего лишь таблицу, из которой берутся значения для цветовых регистров, создается последовательно, начиная с 0 и заканчивая 15. Значение элемента изо- бражения 0 выберет цветовой регистр 0, который в большинстве случаев явля- ется черным. Значение элемента изображения 15 выберет цветовой регистр 15 и т. д. Загрузка значений RGB в цветовые регистры VGA описана в гл. 1. Так же, как описано в гл. 1, адаптер EGA способен выводить на экран одно- временно 16 цветов полной палитры из 64 цветов. Формат описания цвета задан как r’g’b’ RGB где биты R, G и В отнесены как биты основных цветов, а г’, g’ и Ь’ — биты переменных цветов. Если в байте компонента цвета установлен любой четный бит, это означает, что бит основного цвета имеет значение 1. Если установлен любой из нечетных битов, тогда бит переменного цвета имеет значение 1. Если установлены и четный, и нечетный биты, оба бита — основной и переменный —
Формат графических файлов РСХ/РСС 215 имеют значение 1. Чтобы определить, к какому из 64 возможных цветов отно- сится компонента палитры, необходимо применить это правило к каждому из байтов RGB в компоненте палитры файла PCX. Например, если 16-ричные зна- чения компонент RGB равны 55, АА и FF соответственно, описатель цвета будет равен: Описатель Красный = 55 => только нечетные биты Зеленый = АА => только четные биты Синий = FF => четный и нечетный биты Общее значение цвета г’ g’ b’ R G В 1 0 установлен переиенный бит О 1 установлен основной бит 1 1 оба бита установлены 10101 = Цвет 43 Таким образом, для этого компонента палитры PCX палитра EGA будет уста- новлена на цвет 43. {Примечание: равносильным тому же цвету компонентом палитры RGB будет 01, 02, ОЗЬех. Это удовлетворяет тем же критериям четных и нечетных битов. Подумайте об этом.) Для изображений CGA в четырех цветах красная составляющая первой ком- поненты палитры, деленная на 16, является цветом фона (от 0 до 15), а красная составляющая второй компоненты палитры, деленная на 32, является идентифи- катором палитры CGA (от 0 до 7, хотя стандартный дисплей CGA поддерживает только палитры от 0 до 3). Остальные данные палитры не воспринимаются. Если для вывода изображения требуется более 16 цветов, необходимо ис- пользовать в PCX-файле другой механизм палитры. Этот альтернативный меха- низм называется расширенной палитрой. Поскольку заголовок PCX-файла имеет фиксированную длину и формат и область хранения палитры внутри заголов- ка ограничена общим количеством 48 байт, то, если требуется более 16 цветов, необходимо отвести в PCX-файле новую область палитры. Все изображения, со- зданные в режиме VGA 16 hex (с 256 возможными цветами, записываемыми в формате PCX-файла), требуют расширенной палитры. Расширенная палитра — это дополнительная структура данных, присоеди- ненная к концу PCX-файла после данных растрового изображения. Формат этой структуры данных следующий: /♦ Дополнительная структура данных расширенной палитры для РСХ-файлов с 256 цветами ♦/ struct ExtendedPalette BYTE ExtendedPalette; ColorRegister Palette Dfax256PaletteColors]; /♦ Максимальный размер 768 байт ♦/ 1; Первым полем в этой структуре является метка, определяющая расширен- ную палитру. Для действительной структуры расширенной палитры она всегда
216 Глава 6. Форматы и функции графических файлов равна десятичному числу 12. За тегом находятся позиции хранения для 256 трех- байтных компонент RGB: в сумме 768 байт. Когда структура расширенной па- литры считывается из PCX-файла, эти компоненты RGB переносятся непосред- ственно в 256-цветовые регистры VGA. Библиотека функций PCX поддерживает расширенную палитру как при чтении, так и при записи РСХ-файлов. Концепция расширенной палитры иллюстрирует ограниченность формата графического файла с фиксированной структурой. Расширенную палитру при- ходится присоединять к концу структуры PCX-файла; иначе бы она полностью изменила фиксированный формат РСХ-файлов изображения. Такие изменения привели бы к тому, что ни одна из существующих прикладных программ не смо- гла бы прочитать новые PCX-файлы. Это создало бы серьезную проблему для всех прикладных программ, поддерживающих PCX-файлы. Помещение структу- ры расширенной палитры в конце файла приводит к тому, что все прикладные программы, понимающие расширенные палитры, могут найти их, а те приклад- ные программы, которые не понимают их, могут их пропустить. Хотя эта схема работает, она может создать трудности для программ, читаю- щих PCX-файлы. Программа «view.exe», рассмотренная ниже (стр. 256), сталки- вается с той же проблемой. Это означает, что факт отсутствия или присутствия расширенной палитры не известен до тех пор, пока все данные растрового изо- бражения не будут считаны из файла. Если растровые данные непосредственно переносятся на видеодисплей в том виде, в котором они прочитаны, изображение будет выводиться на дисплей в причудливых цветах (по умолчанию использует- ся палитра режима дисплея) до тех пор, пока не будет проверено присутствие структуры расширенной палитры, а ее содержание прочитано и записано на ада- птер VGA. Только после того, как полное изображение будет записано на экран, палитра VGA заменяется на цвета, требуемые для данного изображения. Только после этого изображение выводится на дисплей в правильных цветах. Возможным путем решения этой проблемы было бы перемещение в конец PCX-файла, чтобы отсчитать обратно резервные 769 байт и попытаться найти метку расширенной палитры. Бели она найдена, расширенная палитра может быть прочитана в графический адаптер VGA до того, как будут прочитаны дан- ные растра. Это приведет к тому, что изображение при его выводе на дисплей будет иметь правильные цвета. Мы здесь представили лишь один из способов решения проблемы. Что произойдет, если цотребуется снова расширить формат PCX-файла? Но- вое расширение тоже необходимо поместить в конец файла по изложенным вы- ше причинам. Обратный отсчет 769 байт от конца PCX-файла, предназначенных для поиска расширенной палитры, приведет к неправильному сдвигу. Из этого обсуждения вы должны понять смысл и ограничения фиксированных форма- тов файлов. Для устранения подобного типа трудностей был разработан формат TIFF (обсуждается в этой главе ниже). Для завершения обсуждения палитр РСХ-файлов необходимо отметить одну важную вещь. Значения компонент RGB, содержащиеся в палитре РСХ-файлов (как она хранится в файле на диске), в четыре раза больше их истинных зна- чений. Другими словами, диапазон значений цветовых компонент, записанных в PCX-файле, составляет от 0 до 255, тогда как значения цветовых регистров VGA должны быть в диапазоне от 0 до 63. По этой причине, когда библиотека функ- ций PCX читает PCX-файл, перед загрузкой в цветовые регистры VGA значения цветов палитры делятся на четыре. И наоборот, перед помещением в структуру
Формат графических файлов РСХ/РСС 217 палитры PCX при подготовке к записи в РСХ-файл значения цветовых регистров умножаются на четыре. Причина такого изменения масштаба значений цвета — в анахронизме, оставшемся от процедуры обращения с PCX-файлами EGA. Сжатие по методу ограничения длины прохода Необходимость сжатия данных становится очевидной, если принять во внимание объем памяти, необходимый для хранения изображения. Например, для хране- ния полутонового изображения 640 х 480, создаваемого видеопреобразователем, требуется 307200 байт. Сканер с разрешением 300 точек на дюйм по вертикали и столько же по горизонтали, используя восемь бит на элемент изображения, мо- жет создать файл размером более 8 Мбайт. Объемы памяти, требующиеся для хранения изображений в виде битового массива, приведены в табл. 6.1. Немного файлов изображений такого размера достаточно, чтобы заполнить самый боль- шой жесткий диск. Таблица 6.1. Объемы памяти, требующиеся для хранения изображений в виде би- тового массива Разрешение в точках на д юйм Биты на элемент изображения 1 4 8 24 72 60588 242352 484704 1454112 150 262969 1051875 2103750 6311250 300 1051875 4207500 8415000 25245000 Примечания а) Представленные данные предполагают, что все изображения имеют размер 8,5 X 11 дюйм. б) Горизонтальное и вертикальное разрешения одинаковы. в) В таблице приведено общее количество байтов, необходимое для хранения несжатого изо- бражения при данном разрешении. Степень сжатия, достигаемая методом ограничения длины прохода RLL (Run Length Limited), зависит от файла изображения, но обычно уменьшает требуе- мый объем памяти вдвое-вчетверо. Это достаточно значительное уменьшение для такого простого метода сжатия. В других методах достигаются еще боль- шие степени сжатия (например, алгоритмы Хафмана и LZW), но их недостат- ком является большое количество расчетов (см. статью по методам LZW в ок- тябрьском номере Dr. Dob b’s Journal за 1989 г.). Метод RLL является хорошим компромиссом между размером изображения и временами сжатия/расширения. При RLL-сжатии каждая строка данных изображения сжимается отдельно. Если в изображении есть много битовых плоскостей, сжатые данные состоят из строк, по одной для каждой из составляющих плоскостей. PCX-формат поддер- живает до четырех битовых плоскостей. Порядок битовых плоскостей — СЗКИ, т. е. строка данных синего, строка данных зеленого, строка данных красного и, наконец, строка данных интенсивности. RLL-сжатие не затрагивает строку развертки, но сжимает цветовые составляющие строки развертки. Если изобрел жение не использует много битовых плоскостей, данные изображения являются индексом палитры для каждого значения элемента изображения в строке раз-
218 Глава. 6. Форматы и функции графических файлов вертки. Данные в строке сжимаются путем замены последовательности повто- ряющихся битов байтом счетчика повторений и байтом данных. Байт счетчика повторений идентифицируется по двум своим старшим значащим битам, устано- вленным на 1. Шесть его младших битов определяют значение счетчика повто- рений от 1 до 63 для следующего за ним байта данных. Бели байт данных имеет установленные оба старших бита, он также должен быть закодирован как байт счетчика повторений и байт данных. Псевдокод для RLL-сжатия следующий: Функция CompressScanLine Рассчитать общее число байтов для сжатия Repeat Получить символ байта входных данных Прирастить входной указатель Установить счетчик повторений на 1 While следующие входные данные совпадают и максимальное значение счетчика повторений 63 не превышено и еще есть входные данные для сжатия Прирастить значение счетчика повторений EndVhile If значение счетчика повторений больше 1 или символ имеет два установленных старших значащих бита тогда Установить два старших значащих бита в счетчике повторений Записать значение счетчика повторений в выходной файл Endlf Записать байт данных в файл Until Все байты сжаты End Код на языке Си для алгоритма RLL-сжатия содержится в функции «Сош- pressScanLine» из библиотеки функций PCX. Эта функция показана в листинге 6.1. Версия «CompressScanLine» на языке ассемблера, называемая «Раск», пред- ставлена в листинге 6.2. Листинг 6.2. RLL-кодировщик на языке ассемблера Далее следует содержимое файла compress.asm: ; Процедура сжатия изображения, вызываемая из языка Си ; написана Крейгом А. Линдли ; Последние изменения внесены: 05/22/89 .TEXT segment byte public ’CODE’ DGROUP group .DATA,.BSS assume cs:.TEXT,ds:DGROUP,ss:DGROUP .TEXT ends .DATA segment word public ’DATA’ .DATA ends
Формат графических файлов РСХ/РСС 219 .BSS segment word public ’BSS’ .BSS ends .TEXT segment byte public ’CODE’ Сжатие строки Процедура .Pack Эта процедура сжинает видеостроку по методу RLL. Этот метод» в частности, используется для сжатия РСХ-файлов. При этом методе сжатия последователь- ность повторяющихся байтов сжимается в счетчик повторений, за которым следует повторяющийся байт. Допускается максимальное число повторений, равное 63, поскольку байт счетчика повторений идентифицируется по двум установленным старшим битам. Это также означает, что любой символ, значение которого превышает OBFH, будет закодирован в виде счетчика повторений (С1), за которым следует значение байта, поэтому подобные значения не будут ошибочно приниматься за счетчик повторений. CALL: вызывается из Си. PROTOTYPE: unsigned Pack (BYTE far ♦, BYTE far ♦, unsigned Count) INPUT: все параметры, передаваемые этой функции, находятся в стеке. Стек должен содержать следующие значения: Count по адресу [Ьр+12], сегмент буфера вывода в [Ьр+1О] , смещение буфера вывода в [Ьр+8] , сегмент буфера ввода в [Ьр+6] и смещение буфера ввода в [Ьр+4]. OUTPUT: количество байт в запакованном буфере вывода возвращается в регистр ах. USES: регистры ах, bx, ex, dx, es, si и di. Public .Pack .Pack proc near push bp mov bp,sp push si push di mov si,[bp+4] ; загружаем параметры из стека mov ds,[bp+6] ; указывает на упаковываемый буфер mov di,[bp+8] mov es,[bp+10] ; указывает на буфер вывода mov dx,[bp+12] ; размер буфера вывода выполняем сжатие по методу RLL cld ; перемещаем указатели в память packl: lodsb ; получаем байт из буфера ввода mov bl, al ; записываем его в bl mov cx,l ; счетчик символов равен 1 dec dx ; декрементируем счетчик обрабатываемых байтов
220 Глава 6. Форматы и функции графических файлов Листинг 6.2. (Продолжение) pack2: сюр dx,0 ;вся строка упакована ? je раскЗ ;если да, то переход сюр ex, 63 ;не превышено ли максимальное число повторений ? je раскЗ ;если да, то переход сюр bl, [si] ; переходим к следующему байту jne раскЗ ;если нет повторения, то переход inc si ; переводим указатель на следующий байт в буфере ввода dec dx уменьшаем счетчик оставшихся байтов inc ex ;поскольку мы работаем с повторяющимся байтом, ; увеличиваем значение счетчика повторений j«p short pack2 ; проверяем следующий байт ; можно записывать сжатую последовательность в буфер вывода ; раскЗ: сюр сх,1 ; значение счетчика повторений > 1 ? ja раскб ;если да, то переход сюр al,0BFH ; значение символа > OBFH ? ja раскб ;если да, то переход ;иы имеем один байт со значением, не превышающим ОСОН. раск4: st о sb ; помещаем байт в буфер вывода сюр dx,0 ;есть ли еще символы в буфере ввода ? jne packl ;если да, то начинаем все сначала jnp short раскб ;сжатие закончено, переходим к завершению ; процедуры ; здесь следует образовать пару из счетчика повторений и повторяющегося ; байта для записи в буфер вывода раск5: push mov or stosl pop j*P ax ; сохраняем значение счетчика повторений al,cl записываем значение счетчика повторений в al al,ОСОН устанавливаем два старших бита > записываем счетчик повторений в буфер вывода ах зозвращаемся на байт назад short pack4 записываем повторяющийся байт ;сжатие закончено, переходим к завершению процедуры раскб: .Раск .TEXT mov sub POP pop pop ret endp ends ах,di ;получаем указатель на буфер вывода ах,[Ър+8] ;рассчитываем количество байт в буфере вывода di зосстанавливаем значения регистров ;и выходим из программы si Ър end
Формат графических файлов РСХ/РСС 221 Функция «Раск» в этой книге не используется. Она включена, чтобы пока- зать два различных пути выполнения одного и того же действия. Она работает немного быстрее, но менее удобна для переноса, чем версия на языке Си. Поскольку библиотека функций PCX должна иметь возможность читать и записывать PCX-файлы изображений, требуется аналогичная функция RLL- расширения. Эта функция называется «ExpandScanLine». Псевдокод для про- цесса приведен ниже: Функция ExpandScanLine Рассчитать ожидаемое число расширенных байтов Repeat Считать байт данных из файла Если два старших значащих бита установлены, тогда это метка повторения Замаскировать два старших значащих бита, чтобы получить значение счетчика повторений Получить следующий байт данных из файла Записать это число повторений в байте в выходной буфер Else действительный байт данных Записать байт данных в выходной буфер Endif Until все байты расширены Изображения с одной битовой плоскостью требуют другой обработки, чем изображения с многими битовыми плоскостями. Действительно, изображения с одной битовой плоскостью требуют гораздо меньше времени на обработку и поэтому могут быть сжаты и расширены быстрее (см. функции «WritePCXFile»). Изображения, использующие режим VGA 13 hex, режим с 256 цветами, — это изображения с одной битовой плоскостью. Все VGA-изображения с 16 цветами в этой книге используют четыре битовые плоскости. Библиотека функций PCX Библиотека функций PCX содержит 12 функций PCX, восемь из которых предна- значены для использования извне. В табл. 6.2 перечислены функции, доступные извне, и дано краткое описание их действия. * Таблица 6.2. Библиотека функций PCX 1. Загрузка информации палитры из PCX-файла в графиче- ский адаптер VGA. Прототип unsigned InstallPCXFilePalette (void); Параметры не используются.
222 Глава 6. Форматы и функции графических файлов Таблица 6.2. (Продолжение) Действие При запуске эта функция определит, содержит ли ранее загруженный PCX- файл изображения информацию палитры; если да, функция загрузит па- литру и соответственно цветовые регистры графического адаптера VGA. Перед вызовом этой функции графический адаптер VGA должен нахо- диться в соответствующем графическом режиме. В противном случае при входе в правильный графический режим все загруженные цветовые реги- стры будут перезаписаны программой BIOS. Эта функция поддерживает как нормальную палитру PCX с 16 цветами, так и механизмы расширен- ной палитры, описанные выше. 2. Чтение PCX-изображения в буфер памяти. Прототип CompletionCode ReadPCXFileToBuf (char *FileName, BYTE huge**Buffer- Prt); Где «FileName» — имя PCX-файла, который нужно считать в память. «File- Name» должно быть строкой символов, заканчивающейся нулем. Допуска- ется полное имя. «FileName» не требует (или запрещает) расширения фай- ла. Поскольку эта функция вызывает «ReadPCXFileHdr», любое заданное расширение будет удалено и вместо него использовано расширение «.РСХ». «BufferPrt» — указатель на адрес в памяти, куда будут считаны данные изображения из РСХ-файла. Действие Эта функция читает PCX-файл изображения в буфер памяти (отведен- ный в глобальной памяти) и возвращает указатель на изображение. При успешном завершении переменная PCXData будет содержать всю инфор- мацию заголовка изображения, а массив «Color256Palette» — расширенную палитру, если она существует. Текущая палитра VGA и регистры цвета не заменяются непосредственно при вызове этой программы. Для выво- да на дисплей данных PCX-изображения из буфера необходимо вызвать «InstallPCXFilePalette», а затем «DisplaylmagelnBuf». Если эта функция не сможет загрузить данные PCX-изображения, она возвратит различные коды ошибки (определенные в «pcx.h»). Эта функция широко используется кодом обработки изображения, представленным в ч. II. 3. Вывод предварительно загруженного PCX-изображения из буфера памяти на дисплей. Прототип void DisplaylmagelnBuf (BYTE huge *Image,unsigned SetMode, unsigned Pause); Где «Image» — указатель на то место в памяти (в глобальной памяти), где находятся данные изображения. «SetMode» определяет, нужно ли инициализировать видеорежим VGA и палитру перед выводом изображения на дисплей. Если «SetMode» равно TRUE, видеорежим и палитра будут инициализированы как требуется для
Формат графических файлов РСХ/РСС 223 вывода изображения, содержащегося в буфере памяти, перед тем как бу- дет осуществлен вывод изображения. Бели «SetMode» равно FALSE, эта функция предполагает, что видеорежим и палитра правильные, и присту- пает к выводу изображения без инициализации. Для помощи в написании ясных программ определены две константы: «INITVGALOADPALETTE» и «NOVGAINIT». «INITVGALOADPALETTE» определена как TRUE, тогда как «NOVGAINIT» определена как FALSE. «Pause» определяет, должна ли эта функция ждать, пока пользователь на- жмет клавишу на клавиатуре, перед тем как возвратиться к вызывающей программе. Если «Pause» равно TRUE или константе «WAITFORKEY», перед окончанием работы этой функции пользователь должен нажать клавишу. Если «Pause» равно FALSE или «NOWAITFORKEY», возврат из этой функции происходит немедленно после вывода изображения на дисплей. Действие Эта функция выводит растровое изображение, содержащееся в буфере па- мяти. Поддерживаются три разрешения дисплея: 320 х 200 с 256 цветами и 640 х 200, 640 х 350 и 640 х 480 — каждое с 16 возможными цветами. Пред- полагается, что переменные «ImageWidth» и «ImageHeight» соответствую- щим образом установлены до вызова этой функции. Эти переменные долж- ны быть установлены с помощью вызова функции «ReadPCXFileToBuf», которая помещает изображение в буфер памяти. Данная функция бу- дет использоваться в ч. II книги для вывода на дисплей результата об- работки данных растрового изображения алгоритмами обработки изобра- жений. 4. Вывод PCX-файла на графический, адаптер VGA. Прототип void DisplayPCXFile(char*FileName,intVerbose): Где «FileName» — имя файла в ASCII, как определено выше. «Verbose» — переменная, показывающая, содержится ли подробная инфор- мация об изображении в заголовке PCX-файла, который нужно вывести. Подробно о том, какая PCX-информация будет выведена на дисплей, см. функцию «ReadPCXFileHdr». Действие Эта функция загружает и выводит на дисплей PCX-изображение из файла на диске. Все параметры, необходимые для правильного вывода изобра- жения, берутся из заголовка, считываемого из PCX-файла изображения. Данные изображения берутся непосредственно из файла и переносятся на дисплей VGA. Промежуточный буфер изображения не используется. Лю- бые ошибки, встретившиеся при выполнении этой функции, прекратят вы- полнение программы и приведут к выдаче выходного кода, объясняющего ошибку. 5. Запись в PCX-файл изображения, содержащегося в буфере памяти. Прототип CompletionCode WritePCXFileFromBuf (char*FileName, BYTE huge*Image- Memory);
224 Глава 6. Форматы и функции графических файлов Таблипд 6.2. (Продолжение) Где «FileName» — как определено выше. «ImageMemory» — указатель на то место в памяти, где находятся данные изображения. Действие Эта функция подобна функции «WritePCXFile» (описанной ниже), за ис- ключением того, что она берет свои растровые данные из буфера в па- мяти, а не из дисплея. Параметры, управляющие типом записываемого PCX-изображения, берутся из структуры «PCXData» (предварительно счи- танной из РСХ-файла), а не из параметров, передаваемых этой функции. Палитра, в данный момент используемая графическим адаптером VGA, записывается в РСХ-файл. Эта функция поддерживает механизмы как нормальной, так и расширенной палитры. При завершении работы по- сле записи РСХ-файла на диск эта функция освобождает память, свя- занную с буфером изображения (предварительно отведенную функцией «ReadPCXFileToBuf»). 6. Запись е РСХ-файл изображения, е данный момент выве- денного на дисплей VGA. Прототип void WritePCXFile(char*FileName, unsigned BitsPerPixel, unsigned MaxX, unsigned MaxY, unsigned Planes, unsigned BytesPerLine); Где «FileName» — как определено выше. «BitsPerPixel» показывает число бит на элемент изображения в битовой плоскости для каждого элемента изображения, который требуется запи- сать в РСХ-файл. Все поддерживаемые разрешения изображения, кроме 320 х 200 с 256 цветами, используют параметр «BitPerPixel», равный 1. Ка- ждый из этих типов изображений использует четыре битовые плоскости для общего числа бит на элемент изображения, равного 4, или 16 возмож- ных цветов. Режим с 256 цветами, являясь изображением с одной битовой плоскостью, использует для «BitsPerPixel» значение 8. «МахХ & MaxY» — размеры изображения, записываемого в виде РСХ- файла. Обычно они равны разрешению режима VGA, используемого для вывода изображения на дисплей. «Planes» — число битовых плоскостей, используемых для представления изображения. Значение этого параметра равно 1 для изображений с 256 цветами или 4 для изображений с 16 цветами. «BytesPerLine» определяет, сколько байт составляют одну строку развертки изображения. Для изобра- жений 320 х 200 «BytesPerLine» равно 320. Для всех других изображений, использующих четыре битовые плоскости, значение этого параметра равно 80 (ширина 640 элементов/8 элементов на байт). Действие Эта функция создает РСХ-файл для изображения, в данный момент нахо- дящегося на дисплее VGA. Она записывает заголовок РСХ-файла, а после него — растровые данные, за которыми может следовать структура рас- ширенной палитры. Тип создаваемого РСХ-файла зависит от параметров, передаваемых этой функции.
Формат графических файлов РСХ/РСС 225 7. Чтение несжатого файла изображения в буфер памяти. Прототип CompletionCode ReadRawImageFileToBuf (char *FileName, unsigned ImageWidth, unsigned ImageHeight, BYTE huge* *BufferPrt); Где «FileName* — как определено выше. «ImageWidth & ImageHeight* описывают размеры изображения и, следова- тельно, количество байтов необработанных данных, которые следует счи- тать из файла на диске. «BufferPtr» — указатель на то место, где должен храниться адрес данных изображения, считанных из файла необработанных данных. Действие Эта функция, не связанная с PCX-файлами, включена в библиотеку функ- ций, чтобы все функции ввода/вывода файлов находились в одном месте. Она читает файл необработанных данных изображения в буфер памяти, который она отводит этому изображению. Количество данных, читаемое из файла, определяется заданными размерами изображения. При ошиб- ке в чтении файла изображения возвращаются различные коды ошибки. Если чтение прошло успешно, указатель на данные изображения и состо- яние «NoError» возвращаются вызывающему коду. Эта функция не делает предположений о формате данных, читаемых из файла, кроме того, что они должны храниться в последовательных местах памяти буфера изо- бражения. Все задумано так, что код, обрабатывающий буфер изображе- ния, поймет эти данные. Другими словами, важно, чтобы код, записыва- ющий необработанные данные в файл, и код, обрабатывающий буфер изо- бражения, имели согласованный формат данных. Если две операции не согласуются по формату, правильный обмен данными изображения будет невозможен. 8. Запись несжатых данных изображения из буфера памяти на диск. Прототип CompletionCode WriteRawImageFileFromBuf (char *FileName, unsigned ImageWidth, unsigned ImageHeight, unsigned Transpose, BYTE huge *ImageBuffer); Где все параметры, кроме следующих, определены: «Transpose» определяет, какой из двух форматов выходных данных должен быть записан в файл. Если «Transpose* равно FALSE, данные в файле будут организованы столбец за столбцом, в таком же формате, в каком они хра- нятся в буфере изображения, заполняемом видеопреобразователем. Если он равен TRUE, перед записью в выходной файл данные будут расположе- ны так, что окажутся в построчном растровом формате. «ImageBuffer* — указатель типа HUGE на данные изображения, которые будут записаны на диск. 15-3
226 Глава 6. Форматы и функции графических файлов Таблица 6.2. (Продолжение) Действие Эта функция записывает необработанные данные изображения из буфе- ра в памяти в файл на диске. Она поддерживает два различных формата выходных данных, что определяется параметром «Transpose», передавае- мым этой функции. Используемый формат выходного файла зависит от прикладной программы. Если возникают проблемы при записи выходного файла, эта функция возвращает различные коды ошибки. Хотя мы при- водим код, поддерживающий файлы необработанных изображений, они не используются в этой книге. Действие других функций, представленных в библиотеке функций PCX (функций, которые не предназначены для вызова извне), подробно объ- ясняется в листинге 6.1. Примеры использования библиотеки функций PCX Библиотека функций PCX широко использовалась в гл. 5 для записи оцифро- ванных изображений в виде РСХ-файлов. Для использования библиотеки функ- ций PCX прикладная программа должна содержать PCX-файлы заголовков «pcx.h» и «misc.h» во время компилирования. При включенном файле заголов- ка для создания PCX-файла изображения, выведенного на графический адаптер VGA, требуется лишь вызов функции «WritePCXFile» из прикладной програм- мы (предварительно скомпонованной с файлом «pcx.obj»). Параметры, переда- ваемые функции «WritePCXFile», описаны выше и повторяются для сравнения в табл. 6.3. Таблица 6.3. Параметры вызова функции «WritePCXFile» Разрешение Цвета BitsPerPixel MaxX MaxY Planes BytesPerLine 320 X 200 256 8 320 200 1 320 640 X 200 16 1 640 200 4 80 640 х 350 16 1 640 350 4 80 640 х 480 16 1 640 480 4 80 Для записи изображений, создаваемых видеопреобразователем, требуются три различных типа РСХ-файлов изображений. Это изображения с разреше- нием 320 х 200 с 256 цветами, полутоновые изображения (16 уровней яркости) с разрешением 640 х 200 и с разрешением 640 х 480. В табл. 6.3 представлены параметры, необходимые функции «WritePCXFile» для записи РСХ-файлов с ка- ждым из возможных разрешений. Приведен также режим разрешения 640 х 350, поскольку его поддерживает функция «WritePCXFile», хотя он никогда не ис- пользуется при записи оцифрованных изображений. Пример вывода на дисплей PCX-файла приведен в конце этой главы при об- суждении программы «view.exe». Пример использования библиотеки функций
Формат графических файлов РСХ/РСС 227 PCX для непосредственного вывода на дисплей PCX-изображения вы можете найти в листинге 6.4. Как показано в предыдущем примере, вывод РСХ-файла изображения так же прост, как и запись РСХ-файла. Для этого также требуется единственная строка кода. Чтение и запись РСХ-файлов в память вместо дисплея VGA заслуживают дальнейшего пояснения. Функция «ReadPCXFileToBuf» осуществляет перенос изображения в память (из РСХ-файла) для последующих операций. После их за- вершения для записи изображения обратно в РСХ-файл используется функция «WritePCXFileFromBuf». Эти функции рассмотрены вместе с другими процеду- рами поддержки обработки изображений в ч. II книги. Сейчас, однако, необхо- димо только понять, как правильно вызывать эти функции. Их использование иллюстрирует следующий фрагмент кода. Комментарии должным образом опи- сывают действие кода. /♦ Важные переменные */ char «InFileNane; /* указатель на имя входного РСХ-файла ♦/ char «OutFileName; /♦ указатель на имя выходного РСХ-файла ♦/ BYTE huge «Thelnage; /♦ указатель huge на изображение ♦/ /* Попытаться прочитать РСХ-файл в буфер. Отвести буфер необходимого размера для помещения изображения. Если успешно, установить переменную указателя HUGE, The Inage, указывающую на буфер изображения. Если произошла ошибка, обработать ее соответственно для прикладной программы. ♦/ if (ReadPCXFileToBuf(IhFileNane,tThe Image) != NoError) exit(l); /♦ РСХ-файл успешно прочитан в память. Теперь установить палитру, прочитанную из РСХ-файла, находящегося в работе, для вывода изображения на дисплей. ♦/ InstallPCXFilePaletteO; /♦ Дополнительно вывести на дисплей исходное изображение. */ DisplayInageinBuf (Thelnage); /♦ обработка изображения проводится здесь на данных в буфере ♦/ /♦ Дополнительно вывести на дисплей обработанное изображение. ♦/ DisplayInageinBuf (Thelnage); /* Записать обработанное изображение в виде РСХ-файла. Снова соответствующим образом обработать ошибки. Эта функция автоматически освобождает буфер памяти, в котором хранится изображение. ♦/ if (WritePCXFileFromBuf(OutFileName, Thelnage) !» NoError) exit(l); 15*
228 Глава 6. Форматы я функция .графических файлов Потеря информации изображения Важно представлять себе, что когда PCX-файл изображения создается с экрана VGA с 16 цветами, некоторая часть информации изображения при этом теряется. Чтобы понять, почему это происходит, вернемся к обсуждению демонстрацион- ных программ в гл. 5. Вспомним, что масштаб данных изображения для вывода на дисплей должен быть изменен от диапазона 0—63 (создаваемого видеопреобра- зователем) до 0-15. Когда PCX-файл записывается с экрана с использованием этих данных с измененным масштабом, динамический диапазон исходного изо- бражения будет частично потерян. Созданный PCX-файл содержит 4 бит на эле- мент информации изображения (все, что необходимо для 16 возможных цветов), тогда как исходные данные содержат 6 бит. Для предотвращения этих потерь можно воспользоваться двумя методика- ми: читать и записывать в случае изображений с 16 цветами файлы необра- ботанных данных изображения, а не PCX-файлы данных, или для записанных РСХ-файлов, которые будут использоваться для обмена данными изображений между прикладными программами, выбирать только режим 320 х 200 с 256 цве- тами. Функции «ReadRawImageFileToBuf» и «WriteRawImageFileFromBuf» из би- блиотеки функций PCX обеспечивают возможность обмена необработанными данными изображений между прикладными программами. Другими словами, 6 бит данных изображения, записанные в файл необработанного изображения, равны 6 бит данных изображения, прочитанных из файла; при этом содержание информации изображения остается неизменным. К недостаткам использования файлов необработанных изображений следует отнести: 1) данный формат не бу- дут понимать коммерческие прикладные программы; 2) размер файлов может быть чрезмерно большим. Таким образом, если очень важна «верность» изобра- жения и все прикладные программы, использующие изображения, сделаны на заказ (под контролем программиста), разумно применять методику обмена не- обработанными данными изображения. Если вашим требованиям удовлетворяют изображения с разрешением 320 х 200, для обмена данными между прикладными программами можно использо- вать PCX-файлы без боязни потерять информацию данных изображения. Это возможно, поскольку все изображения с этим разрешением (созданные кодом, представленным в книге) хранятся с 8 бит на элемент изображения. Это озна- чает, что исходное 6-бит содержание каждого элемента изображения легко со- храняется в РСХ-файле. Формат графических файлов TIFF Введение Формат TIFF (Tagged Image File Format) был разработан, чтобы устранить те трудности, которые связаны с фиксированными форматами файлов. (Некото- рые проблемы с расширяемостью формата PCX были описаны выше в главе.) Ключевым словом здесь является «разработан». Формат TIFF появился не в процессе развития уже имеющегося стандарта. Он создавался, чтобы стать про- мышленным стандартом для обмена файлами изображений. Формат TIFF явля-
Формат графических файлов TIFF 229 ется надмножеством всех существующих графических форматов или форматов файлов изображений. Он достаточно «гибок», чтобы исключить необходимость или оправдание собственных графических форматов. Фактически имеется воз- можность записать специфическую информацию в TIFF-файл без нарушения содержания формата. TIFF разрабатывался для применения не только в на- стоящее время, но и в будущем. Разработчики формата TIFF преследовали три важные цели: 1. Расширяемость. Это способность добавлять новые типы изображений, оставляя действительными старые, а также добавлять к формату новые информационные поля, не влияя на способность старых прикладных про- грамм считывать файлы изображений. 2. Переносимость. Формат TIFF разрабатывался так, чтобы быть независи- мым от типа аппаратных средств и операционной системы. Он предъявля- ет очень незначительные требования к операционной системе, в которой будет работать. Формат TIFF должен выполняться (и выполняется) оди- наково хорошо как на IBM PC, так и на Apple Macintosh. 3. Обратимость. TIFF разрабатывался не только для того, чтобы быть эф- фективной средой для обмена графической информацией, но и для ис- пользования в качестве собственного внутреннего формата данных для программ редактирования изображений. Правильность выбранной цели подтверждается количеством продавцов про- граммного обеспечения, которые поддерживают формат TIFF. Все крупные про- изводители сканеров и программ электронной верстки поддерживают этот фор- мат. С каждым днем его поддерживает все большее число прикладных программ. Кроме того, появляются многочисленные программы-трансляторы, которые пе- реводят другие графические форматы в формат TIFF. Богатство формата TIFF решает многие проблемы, но в то же время созда- ет свои собственные. Структура файла в формате TIFF неизбежно будет слож- ной — в действительности, более сложной, чем во многих частных форматах, для замены которых и разрабатывался формат TIFF. В связи с повышенной слож- ностью формата для управления им требуется значительно больше кода, чем в большинстве других графических форматов. Это приводит к тому, что возрастает время считывания и записи файлов, а также время, необходимое на разработку программ в формате TIFF. Тем не менее программы, представленные в книге, резко сократят вам время разработки, поскольку вся самая трудная работа за вас уже проделана. Чтобы провести сравнение программ по их сложности, мож- но посчитать количество строк программного кода. Библиотека функций PCX, представленная выше, содержит примерно 2600 строк, в то время как библиотека функций TIFF содержит примерно 13500 строк. Таким образом, для поддержки формата TIFF требуется в пять раз больше кода. Библиотека функций TIFF поддерживает версию TIFF 5.0. Этот формат управляется спецификацией, написанной совместно Aldus Corporation и Microsoft: Кроме того, несколько других компаний внесли свой вклад в формулировку фор- мата TIFF. Версия 5.0 этой спецификации выпущена в апреле 1988 г. и ее можно найти в приложении 2. Она включена в книгу с разрешения Aldus Corporation. Хотя авторские права на саму спецификацию принадлежат Aldus Corporation,
230 Глава 6. Форматы и функция графических файлов формат TIFF является общедоступным. Это означает, что за использование фор- мата не надо платить. Полная спецификация включена в книгу, поскольку мо- жет быть полезна в качестве справочника. Спецификация и приложения к ней, помимо информации, касающейся формата TIFF, содержат много ценных сведе- ний по общим вопросам воспроизведения изображений. Поскольку специфика- ция здесь представлена и, в отличие от большинства других спецификаций, она достаточно легко читается, обсуждение, которое следует ниже, касается только функциональности формата TIFF. В спецификации вы сможете найти ответы на любые вопросы относительно формата TIFF. И наконец, по формату TIFF было написано несколько хороших статей. Сведения об этих статьях можно найти в разделе «Литература». Структура файлов в формате TIFF Файлы в формате TIFF состоят из трех отдельных структур данных. Структуры, а также их функции, показаны на рис. 6.2. Пожалуйста, обращайтесь к этому рисунку во время обсуждения. Первая структура данных, которая находится в любом файле TIFF, называ- ется Заголовком файла изображений, или структурой IFH. Она является един- ственной частью TIFF-файла, которая имеет фиксированное положение. Эта 8- байтовая структура должна размещаться в файле, начиная с нулевого смещения. IFH содержит важную информацию, необходимую для правильной интерпрета- ции остальной части TIFF-файла. Первое и, возможно, наиболее важное поле IFH, является полем упорядочения байтов. Оно показывает порядок располо- жения байтов, который использовался при создании TIFF-файла. Это особенно важно, поскольку TIFF-файлы могут переноситься между компьютерами IBM PC (основанными на процессорах Intel) и Macintosh (основанными на процес- сорах семейства Motorola). Как вы, возможно, знаете, способы упорядочения байтов (в рамках 16-разрядных и длинных 32-разрядных целых чисел), кото- рые используются процессорами Intel и Motorola, значительно отличаются. На рис. 6.3 показаны эти различия. Надежда на то, что считывающая программа восстановит изображение, будет не велика, если эта программа не знает порядка байтов, который использовался при создании файла изображений. Упорядоче- ние влияет на любые данные длиной более одного байта, которые считываются с TIFF-файла. Поле упорядочения байтов в IFH содержит либо два символа М (hex 4D4D), показывая формат целых чисел процессора Motorola, либо два символа I (hex 4949), показывая формат процессора Intel. Использование сдвоенных байтов по- зволяет правильно считывать это поле с помощью процессора любого типа. Если упорядочение байтов, используемое в файле, отличается от используемого ком- пьютером, на котором считывается файл изображений, все последовательные 16- и 32-битовые целочисленные значения должны будут поменять свое упоря- дочение байтов. Если упорядочение байтов одинаковое, значение целого числа и числа типа «long» могут быть считаны и использованы без модификации. К счастью, все современные методы уплотнения данных, использующие стандарт TIFF, имеют байтовую организацию. Если бы использовались методы уплотне- ния с организацией по другим размерам, байтовое упорядочение данных изо-
Формат графических файлов TIFF 231 бражения пришлось бы учитывать в процессе считывания данных из файла. Изменение порядка следования байтов в каждом слове из уплотненных данных изображения по мере считывания файла занимало бы значительное машинное время. Поле версии в IFH всегда содержит десятичное значение 42. Это поле можно использовать, чтобы проверить, имеет ли файл формат TIFF. Указанное чи- сло не является номером версии программного обеспечения формата TIFF, как можно было бы ожидать. В действительности это число, вероятно, никогда не изменится. Если же оно будет другим, это покажет, что формат TIFF-файла радикально изменился и считывающей программе не следует даже пытаться считывать файл. Значительные изменения в формате TIFF, которые делали бы необходимым изменение поля версии, находятся в противоречии с философией расширяемости этого формата. Последнее поле в структуре IFH содержит смещение в байтах от начала фай- ла до структуры, которая называется Каталогом файлов изображений или IFD. Эта структура является второй из трех структур данных, содержащихся в TIFF- файле. Отметим, что, хотя на рис. 6.2 и показано, что в TIFF-файле IFD непо- средственно следует за IFH, это не обязательно так. Программа, считывающая файл в формате TIFF, никогда не должна этого предполагать, а должна прове- сти поиск определенного значения смещения, чтобы найти в файле изображений первую структуру IFD. При этом не важно, следует ли она в файле после струк- туры IFH или нет. В TIFF-файле может находиться одна или несколько структур IFD. Каждая структура должна размещаться на границе слова. Бели имеется больше одной структуры IFD, файл содержит больше одного изображения. Считывающие про- граммы должны иметь доступ к любому числу изображений в файле формата TIFF. В действительности лишь очень немногие считывающие программы имеют такие большие возможности, как программа, представленная в этой главе. Структура IFD включает значение TV, соответствующее числу элементов ка- талога, которые следуют за ним, N 12-байтовых элементов каталога и, наконец, еще одно значение смещения. Место записи смещения в структуре IFD будет содержать смещение от начала TIFF-файла до следующей структуры IFD или четыре нулевых байта, если эта структура является последней в файле. Элемен- ты каталога в IFD отсортированы по возрастанию с помощью значения тега. Это помогает уменьшить объем кода при обработке файлов в формате TIFF. Последней структурой данных в формате TIFF является Элемент каталога или структура DE. Она представляет собой формат элементов каталога, кото- рый обеспечивает гибкость TIFF. Как уже отмечалось, каждый элемент катало- га имеет длину 12 байт и сегментируется в четыре поля, показанные на рис. 6.2. Первым полем структуры DE является поле тега. Как показывает полное на- звание данного формата файлов изображения, тег является фундаментом, на котором основана структура файлов. Растровые данные, содержащиеся в TIFF- файле, определяются тегами, к которым они прикреплены. Файлы изображений с теговой организацией имеют следующие преимущества: а) Прикладные программы, например программы, считывающие файлы в формате TIFF, могут благополучно игнорировать любые теги, которые они не понимают.
232 Глава 6. Форматы и функции графических файлов 8-байтовая структура, расположенная в начале файла изображения Каталог Файла Изображения
Формат графических файлов TIFF 233 б) В любое время можно добавить новые теги, не повреждая при этом ста- рых. В некотором смысле это не дает устареть формату TIFF. в) Чтобы хранить частную информацию в файле TIFF, не влияя при этом на другие прикладные программы, можно определить приватные теги. Теги со значениями между 32768 и 65535 зарезервированы для этой цели. В версии 5.0 спецификации TIFF определяется всего 45 тегов. Эти теги при- ведены в табл. 6.4. Числовое значение тега показывает, какой параметр изображения содержит- ся в элементе каталога. Теги между нулем и 32767 зарезервированы для исполь- зования общими полями TIFF, в то время как теги от 32768 до 65535 зарезер- вированы для приватных полей. Чтобы не допустить использования продавцами программного обеспечения различными способами одних и тех же тегов, приват- ные поля должны размещаться фирмами Aldus и/или Microsoft. Поле типа показывает типы данных параметра изображения. Спецификаци- ей TIFF в настоящее время определяется пять отдельных типов данных. Ка- ждый тип данных определяется целым числом, которое записывается в поле типа структуры DE. Типы данных представлены ниже: Тип тега Тип данных Описание типа BYTE 1 8-битовое целое число без знака ASCII 2 8-битовые коды ASCII заканчиваются нулевым символом (hex 0) SHORT 3 16-битовое (2-байтовое) целое без знака LONG 4 32-битовое (4-байтовое) целое без знака RATIONAL 5 Два значения LONG. Первое представляет собой числитель, второе знаменатель Поле длины (иногда называют полем счета) структуры DE содержит пре- дусмотренное число групп определенного типа данных. Оно специфицируется в терминах типа данных, а не общего числа байтов, необходимых для записи. Рис. 6.2. Структура файлов в формате TIFF. Примечания 1. Заголовок файлов изображений должен размещаться со смещением 0. 2. Упорядочение байтов «II» (hex 4949) указывает на файл, созданный процессором Intel. «ММ» (hex 4D4D) указывает на файл, созданный процессором Motorola. 3. Версией всегда является число 42 (hex 2А), которое никогда не должно изменяться. 4. Не важно, следует ли первая структура IFD после заголовка файлов изображений или нет. Считывающая программа, чтобы найти эту структуру, должна придерживать- ся указателей. 5. Если значение можно вставить в поле смещения значений, оно будет туда помещено. Если же значение не подходит, смещение значения будет содержать указатель (смеще- ние от начала файла) на то место, где значение будет размещаться. 6. Элементы IFD отсортированы по возрастанию с помощью значения тега.
234 Глава 6. Форматы и функция графических файлов 16-бит целые числа стандарта Intel Нумерация битов 7-------------О 7 ----О 8 бит Адрес N Младший 15--------------8 7---------------О 8 бит Адрес N+1 Старший Значащий байт 16-бит целые числа стандарта Motoiola Нумерация битов 8 би1 Адрес N+1 15--------------8 7---------------О 7 ----0 7---------------О / 8 би I Адрес N Старший Младший Значащий байт 32-бит целые числа стандарта Intel Нумерация битов О 15------------------------- 23-----------------16 31 8 бит Адрес N Младший значащий байт 8 бит Адрес N+1 Старший значащий байт 7 О 8 биг Адрес N+2 Младший значащий . байт 7--------------О 8 би! Адрес N+3 Старший значащий байт । Младшее значащее слово Старшее значащее слово 32-бит целые числа стандарта Motorola Нумерация битов 31-------------24 7 —..... О 8 бит Адрес N Старший значащий । байт____________ Старшее з 23-------------16 7 ---О 8 бит Адрес N+1 Младший значащий байт. 1ащее слово 15-------------8 7--------------О 7 0 7 ......—О 8 бит 8 бит Адрес N+2 Адрес N+3 Старший значащий Младший значащий . байт байт Младшее значащее слово Рис. 6.3. Сравнение упорядочения целых чисел для процессоров Intel и Motoiola.
Формат графических файлов TIFF 235 Например, предназначенный для единственной группы данных тег SHORT (тип данных 3) имеет длину 1, а не 2. Последнее поле в структуре DE называется полем «смещения значения». Это поле обычно содержит смещение в файле до действительных данных, связан- ных с тегом. Другими словами, данные, связанные с элементом каталога, не обязательно физически записывать вместе с элементом каталога, их можно раз- местить в любом месте TIFF-файла. При этом, чтобы найти действительные данные, необходимо руководствоваться указателями данных. Бели данные, свя- занные с каким-либо элементом каталога имеют длину 4 байт или меньше, их можно записать непосредственно в поле «смещение значения», а не в то место, которое указано этим полем. Это сделано для того, чтобы увеличить производи- тельность при поиске малых групп данных. Любые данные, записанные в поле «смещение значения», должны оставаться выравненными по левому краю (запи- санными по направлению от старших байтов к младшим). Чтобы определить, ка- кое количество памяти необходимо для группы данных и, следовательно, можно ли ее записать непосредственно в поле «смещение значения», нужно обращаться к полям «длины» и «типа». Если типом данных тега является ASCII, предполагается строка, завершаю- щаяся нулевым символом (hex 0) (ASCIIZ). В отличие от строк на языке Паскаль в данном случае байт длины не предусмотрен. Разумеется, строкой ASCIIZ будет та, которую предполагает язык Си. Поле «длины» для тега типа ASCII учиты- вает нулевой символ. Однако оно не содержит какого-либо заполнения строки ASCII, которое может потребоваться. Растровые данные, содержащиеся в TIFF-файле, организуются в группы строк (или рядов) развертки данных изображений, называемые полосами. Такая организация помогает снизить Потребности в памяти для считывающей програм- мы, поскольку файл изображений не будет резидентной частью программы во всем объеме. Необходим лишь такой объем памяти, который позволит за один раз запоминать одну полосу данных изображения. Многие из ранее написанных про- грамм в формате TIFF давали однополосные изображения, которые были труд- ны для считывания из-за ограничений по памяти. Спецификация рекомендует ограничивать длину полосы 8К байт. Программа, представленная в библиотеке функций TIFF, может управлять полосами с длиной приблизительно 60000 байт. Сжатие данных в формате TIFF Как уже говорилось выше, для записи изображений с поразрядным предста- влением требуется огромный объем памяти. Чтобы уменьшить размер файлов в формате TIFF, спецификация рекомендует поддержку для четырех типов уплот- нения данных. Программа, выполняющая операцию записи в формате TIFF, должна поддерживать по крайней мере один из методов уплотнения. Считы- вающая программа должна уметь понимать все методы уплотнения. Метод, ис- пользуемый для уплотнения изображений, записывается в тег «Compression». Чтобы определить тип расширения данных для использования растровых дан- ных, содержащихся в файле TIFF, считывающая программа должна считать тег «Compression». Отметим, что при уплотнении данных используются только растровые данные изображений. Все другие элементы информации, содержащи-
236 Глава 6. Форматы и функции графических файлов Таблица 6.4. Список тегов формата TIFF № Имя тега Значе- ние тега Тип тега Длина тега 1. Базовые теги BitsPerSample 258 SHORT S amplesPer Pixel 2. ColorMap 320 SHORT 3*(2**BitsPerSample) 3. ColorResponseCurve 301 SHORT 3*(2**BitsPerSample) 4. Compression 259 SHORT 1 5. GrayResponseCurve 291 SHORT 2**BitsPerSample 6. GrayResponseUnit 290 SHORT 1 7. ImageLength 257 SHORT/LONG 1 8. ImageWidth 256 SHORT/LONG 1 9. NewSubfileType 254 LONG 1 10 Photometriclnterp 262 SHORT 1 11. PlanarConfiguration 284 SHORT 1 12. Predictor 317 SHORT 1 13. PrimaryChromaticities 319 RATIONAL 6 14. ResolutionUnit 296 SHORT 1 15. RowsPerStrip 278 SHORT/LONG 1 16. S amplesPer Pixel 277 SHORT 1 17. StripByteCounts 279 SHORT/LONG 18. Tag length = StripsPer- Image for PlanarConfig = 1 SamplesPerPixel * StripsPer- Image for PlanarConfig = 2 StripOffsets 273 SHORT/LONG 19. Tag length = StripsPer- Image for PlanarConfig = 1 SamplesPerPixel * StripsPer- Image for PlanarConfig = 2 WhitePoint 318 RATIONAL 2 20. XResolution 282 RATIONAL 1 21. YResolution 283 RATIONAL 1 22. Информационные теги Artist 315 ASCII ? 23. D at eTime 306 ASCII 20 «YYYY:MM:DD 24. HostComputer 316 ASCII HH:MM:SSO” ? 25. ImageDescription 270 ASCII ? 26. Make 271 ASCII ? 27. Model 272 ASCII ? 28. Software 305 ASCII ? 29. Факсимильные теги Group3options 292 LONG 1 30. Group4options 293 LONG 1
Формат графических файлов TIFF 237 Таблица 6.4. (Продолжение) № Имя тега Значение тега Тип тега Длина тега Теги поиска и записи документов 31. DocumentN аше 269 ASCII ? 32. PageName 285 ASCII ? 33. PageNumber 297 SHORT 2 34. XPosition 286 RATIONAL 1 35. YPosition 287 RATIONAL 1 He рекомендуемые более теги 36. CellLength 265 SHORT 1 37. CellWidth 264 SHORT 1 38. FillOrder 266 SHORT 1 39. FreeByteCount 289 LONG ? 40. FreeOffsets 288 LONG ? 41. MaxS ample Value 281 SHORT SamplesPerPixel 42. MinS ample Value 280 SHORT SamplesPerPixel 43. SubfileType 255 SHORT 1 44. Orientation 274 SHORT 1 45. Thresholding 263 SHORT 1 Примечания а) Длина тега в действительности означает число групп данных текущего типа тега, а не длину в байтах. б) Знак вопроса означает переменное число элементов данных. с) Некоторые теги могут иметь тип SHORT или LONG в соответствии со спецификацией TIFF. еся в TIFF-файле (структуры IFH, IFD и DE), в любом случае не уплотняются. Методы уплотнения и их значения тега приводятся ниже: Значение тега «Compression» Тип сжатия 1 Сжатия не происходит, но байты упаковываются плотно 2 CCITT Group 3 1-мерный модифицированный метод Huffman (RLL) 3 4 5 32773 Факсимильно-совместимый CCITT Group 3 Факсимильно-совместимый CCITT Group 4 LZW (Lempel-Ziv & Welch) PackBits (Macintosh) Спецификация TIFF не рекомендует записывать изображения в несжатом виде, поскольку при такой записи получается слишком большой размер файла. Программы в библиотеке функций TIFF поддерживают методы 1, 5 и 32773. Me-
238 Глава 6. Форматы и функции графических файлов тод 1 вообще не дает сжатия. В этом методе байты, которые составляют строку данных изображения, плотно упаковываются без внешних битов за исключени- ем, возможно, конца строки. Метод уплотнения данных LZW подробно описан в Дополнении F спецификации TIFF. Метод PackBits также описан в Дополне- нии С этой спецификации. Сама спецификация представлена в Приложении 2 настоящей книги. Классы TIFF Как уже говорилось выше, гибкость формата TIFF создает много проблем для разработчиков программного обеспечения, которые поддерживают этот формат. Для управления всеми имеющимися опциями формат требует значительного объема кода. Прикладная программа, желающая импортировать какое-либо изо- бражение в формате TIFF, не имеет другого выбора, как управлять всеми оп- циями. Это единственный путь, гарантирующий вывод изображения на экран в таком же виде, в каком оно было получено. Для решения, по крайней мере ча- стичного, этих проблем, в версии 5.0 формата TIFF было введено понятие согла- сованных классов TIFF. Прикладная программа может теперь управлять только определенным классом TIFF и нет необходимости в управлении всеми классами. Объявление класса TIFF, поддерживаемого прикладной программой, — обязан- ность самой этой программы. В общем случае файл TIFF может содержать изображения трех различных категорий: а) Черно-белые изображения. В этом случае для вывода каждого элемента изображения используется один бит. б) Полутоновые изображения. На каждый элемент используется от 2 до 8 бит, что дает от 4 до 256 уровней яркости. в) Цветные изображения. Для их вывода используются различные палитры и RGB-компоненты, при этом каждый элемент включает до 24 бит цвето- вых данных. Классы TIFF определяют, какие типы изображений соответствуют каждому классу, а также необходимые для каждого класса теги TIFF. Понятие клас- са TIFF уменьшает гибкость формата TIFF, позволяя, однако программам, вы- полняющим операцию записи, давать изображения, которые могут считываться большинством прикладных программ. Классы TIFF приведены в табл. 6.5. Дан- ная информация взята из Дополнения G в спецификации TIFF. Таблица 6.5. Классы TIFF Определяются четыре класса TIFF: Класс В (называется также TIFF В) для двухуровневых однобитовых изобра- жений Класс G (называется также TIFF G) для полутоновых изображений Класс Р (называется также TIFF Р) для изображений с цветовой палитрой Класс R (называется также TIFF R) для полноцветных изображений Класс X формата TIFF объединяет все четыре класса.
Формат графических файлов TIFF 239 Все классы TIFF должны поддерживать следующие теги: 1. NewSubfileType 2. Image Width. 3. ImageLength 4. RowsPerStrip 5. StripOffset 6. StripByteCounts 7. Xresolution 8. YResolution 9. ResolutionUnit Кроме того, согласованные изображения класса В формата TIFF должны иметь следующие теги со следующими значениями: 1. SamplesPerPixel = 1 2. BitsPerSample = 1 3. Compression = 1, 2 или 32773 (PackBits) 4. Photometriclnterpretation = 0 или 1 Согласованные изображения класса G формата TIFF должны иметь следую- щие теги со следующими значениями: 1. SamplesPerPixel = 1 2. BitsPerSample = 4 или 8 3. Compression = 1 или 5 (LZW) 4. Photometriclnterpretation = 0 или 1 Согласованные изображения класса Р формата TIFF должны иметь следую- щие теги со следующими значениями: 1. SamplesPerPixel = 1 2. BitsPerSample = 1, 2, 3, 4, 5, 6, 7 или 8 3. Compression = 1 или 5 (LZW) 4. Photometriclnterpretation = 3 5. ColorMap Согласованные изображения класса R формата TIFF должны иметь следую- щие теги со следующими значениями: 1. SamplesPerPixel = 3 2. BitsPerSample = 8, 8 и 8 3. Planar Configuration = 1 или 2 4. Compression = 1 или 5 (LZW) 5. Photometriclnterpretation = 2
240 Глава 6. Форматы и функция графических файлов Библиотека функций TIFF Программы, представленные в библиотеке функций TIFF, обеспечивают под- держку для всех классов TIFF. Тем не менее класс В полностью не поддержива- ется, поскольку не предусмотрены алгоритмы уплотнения CCITT, которые ред- ко используются для воспроизведения изображений. Эти алгоритмы специально приспособлены только для двухуровневых, черно-белых изображений. Однако черно-белые изображения могут записываться и считываться с использованием метода уплотнения «PackBits». В том случае, если потребуется алгоритм уплот- нения CCITT, читатель должен его добавить сам. Программы в библиотеке функций TIFF разработаны для того, чтобы устра- нить трудности, связанные с записью и считыванием файлов в формате TIFF. Основные сложности формата будут не видны для пользователя/программиста. Многие операции, которые программисту при использовании менее разработан- ного формата пришлось бы выполнять сознательно, выполняются автоматиче- ски и остаются в тени. Для считывания и записи любого файла изображений в формате TIFF используются только функции, которые определяются в файле «tiffintf.h». Почти все программы, составляющие библиотеку функций TIFF, являются частью библиотеки TIFF, написанной Сэмом Лефлером из Калифорнийского университета в Беркли. Программа уплотнения/расширения LZW также была написана в Калифорнийском университете в Беркли и используется здесь с соот- ветствующего разрешения. Свидетельство об авторских правах имеется в файле «Izw.c». При переносе в систему Turbo С и последующей отладке оригиналь- ных программ выполнялась значительная реорганизация программ библиотеки TIFF. Все исходные программы переводились в формат, который совместим с другими программами, представленными в этой книге. Программы библиотеки функций TIFF являются бесплатными и могут использоваться без предвари- тельного разрешения до тех пор, пока не затрагиваются авторские права. Ниже показаны файлы, составляющие библиотеку функций TIFF. Приводит- ся краткое описание содержания каждого файла. TIFFINFO.DOC Файл содержит дополнительную информацию о вну- тренних функциях библиотеки функций TIFF. Его следует прочитать, перед тем как использовать эту библиотеку. TIFF.H и TIFFIO.H Файлы определяют все структуры данных в форма- те TIFF и внутренние структуры данных, используемые в программах поддерж- ки TIFF. В этих файлах перечислены все теги, которые поддерживаются версией TIFF 5.0. IO.C Файл содержит все программы, которые выполняют считывание и запись на диск данных в формате TIFF. Кроме того, он содержит программу, которая используется для инициализации пакета TIFF. DIR. С Этот файл содержит программу, которая анализирует структуру каталога TIFF (структуры IFH, IFD, DE) при считывании файла и строит струк- туру каталога при записи файлов. Файл включает основную часть самого слож- ного кода. ERROR.С Файл содержит функции, которые выдают предупреждающие сообщения и сообщения об ошибках, когда считывается или записывается не- правильно форматированный файл TIFF. SWAB.С Файл содержит программу, которая выполняет все операции пе- реупорядочения байтов.
Формат графических файлов TIFF 241 PRINT.C Файл содержит сервисную программу для распечатки всей ин- формации по тегам из файла TIFF для проверки пользователем. Она использу- ется программой «view.exe», которая описана ниже. COMPRESS.С Этот короткий файл содержит функцию, выбирающую алгоритм уплотнения, который будет использоваться при считывании и/или за- писи файлов в формате TIFF. DUMPMODE.C Файл имеет программы для управления файлами, кото- рые не используют методов уплотнения для растровых данных. Другими слова- ми, эти функции считывают и записывают несжатые TIFF-файлы. CCITT.C Этот файл содержит заглушки для добавления в библиотеку функций алгоритмов уплотнения CCITT. В соответствующий момент эти за- глушки выдают сообщение об ошибке, указывая на то, что библиотека TIFF не поддерживает эти методы уплотнения. PACKBITS.С Файл содержит программу для считывания и записи фай- лов в формате TIFF, которые используют метод уплотнения 32773. Этот метод называется «Macintosh PackBits». LZW.C Файл содержит программу для считывания и записи растровых дан- ных изображения, уплотненных с использованием метода Lempel-Ziv & Welch. К сожалению, перечисленные выше файлы, входящие в состав библиотеки функций TIFF, имеют слишком большой размер, чтобы их можно было привести в этой книге. Следующие ниже файлы разрабатывались специально для этой книги с це- лью поддержки необходимых для воспроизведения изображений функций ви- деопреобразователя. TIFFINTF.H Файл является интерфейсным файлом, который будет ис- пользоваться всеми прикладными программами, применяющими функции би- блиотеки TIFF. В нем есть все определения функций библиотеки TIFF, поэтому программисту не придется создавать эти определения. Пример того, как исполь- зовать этот интерфейсный файл, приведен в программе «view.exe». TIFF.C В файле даны основные функции доступа к TIFF-файлам, необ- ходимые для считывания и записи этих файлов. Функции аналогичны некоторым функциям из библиотеки функций PCX, которые обсуждались в начале главы. Функции подробно описаны в табл. 6.6 и приведены в листинге 6.3. Таблица 6.6. Библиотека функций TIFF 1. Запись файла TIFF для изображения, воспроизведенного на экране VGA. Прототип CompletionCode WriteTIFFFile (char *FileName,unsigned BitsPerSample, unsigned SamplesPerPixel, unsigned Image Width,unsigned ImageLength); Где «FileName» является именем создаваемого выходного файла TIFF и должен представлять собой ASCIIZ-строку. Вводятся полные составные имена. «FileName» не требует (или запрещает использование) расширения файла. Поскольку эта функция вызывает «TIFFOpen», любое определен- ное расширение файла будет стираться и вместо него будет использоваться расширение «.TIF». 16-3
242 Глава 6, Форматы л функции графических файлов Таблица 6.6. (Продолжение) ♦SamplesPerPixel» контролирует, в каком виде записывается изображе- ние — в одной или нескольких плоскостях. Обычно «SamplesPerPixel» равен 1 и получаемое изображение будет одноплоскостным. Если «SamplesPer- Pixel» равен 4, изображение создается в четырех плоскостях. Изображения в нескольких плоскостях приходится считывать с дискового файла или за- писывать на него значительно дольше, поскольку в этом случае необходима дополнительная обработка. Изображения в нескольких плоскостях поддер- живаются только потому, что такие изображения считывает и записывает программа «ZSoft’s PC PaintBrush 44-». «BitsPerSample» должен равняться 1 для черно-белых изображений, 4 для 16-уровневых полутоновых изображений и 8 для 64-уровневых полутоно- вых изображений или изображений в 256 цветах. Параметры «ImageWidth» и «ImageLength» определяют размеры изображе- ния, которое будет записываться в определенный файл TIFF. Они должны содержать максимальное разрешение для режима, который используется для вывода изображений на экран, т. е. «ImageWidth» должен составлять либо 320, либо 640, a «ImageLength» либо 200, либо 480. Действие Эта функция поддерживает получение только полутоновых и цветных изог бражений и не поддерживает получение черно-белых. От этого у вас не должно создаваться впечатления, что библиотека функций TIFF являет- ся не полной. Такое ограничение имеет только эта функция, а не вся би- блиотека. Черно-белые изображения не поддерживаются потому, что ви- деопреобразователь не дает изображений с одноразрядными элементами. Все изображения, как полутоновые, так и цветные, независимо от числа цветов и уровней яркости будут считываться как файл TIFF с форматом записи «палитра». Это означает, что в выходной файл необходимо записы- вать тег «ColorMap», чтобы считывающие программы могли воссоздавать оригинальные изображения. Подробности об изображениях с форматом за- писи «палитра» приведены в спецификации TIFF в Приложении 2. Функция начинается с вызова «TIFFOpen» и передачи ей имени файла и флага режима записи «w». «TIFFOpen» будет создавать файл в формате TIFF с именем, которое было получено^ и подготовит этот файл для записи. Она также выполняет скрытую инициализацию каталога TIFF, который в конечном счете будет записан на диск как часть изображения в формате TIFF. Предыдущая информация о каталоге стирается и устанавливаются для определенных тегов различные значения по умолчанию. Дополнитель- ную информацию вы найдете в файле «io.c». Если открывается выходной TIFF-файл, для размещения важных па- раметров изображения в заново инициализированную структуру ката- лога многократно вызывается функция «TIFFSetField». В результате в выходном файле будут размещаться теги «SubfileType», «ImageWidth», «ImageLength», «BitsPerSample», «Compression» (в этих программах ис- пользуется только тип уплотнения LZW), «Photometriclnterp», «Маке», «Artist» «HostComputer», «SamplesPerPixel», «XResolution», «YResolution», «PlanarConfiguration» и «ResolutionUnit». По существу, создаются все теги, которые определены в спецификации TIFF как обязательные. После записи этих тегов размещается память для одной строки данных изображения. Данный буфер будет заполняться с дисплея один раз для
Формат графических файлов TIFF 243 каждой строки. Адрес буфера будет передаваться функциям библиотеки TIFF. Функции TIFF проведут уплотнение данных из этого буфера, ис- пользуя метод LZW, и запишут их в выходной файл. Бели число байтов, которые составляют строку данных изображения, из- вестно, можно вычислить число строк в полосе. Вспомним, что длина поло- сы должна составлять 8К байт. Функция, действие которой здесь обсужда- ется, использует это значение для вычисления числа строк, которое следу- ет использовать для составления полосы. Когда это число будет известно, элемент «RowsPerStrip» помещается в каталог TIFF для окончательного размещения в выходном файле. Некоторые программы из-за ограничений по памяти требуют меньшего числа строк в полосе. Для таких программ следует соответственно уменьшить параметр «RowsPerStrip». Затем, чтобы подготовить построение структуры «ColorMap», которая бу- дет записываться в выходной файл, считываются цветовые регистры VGA. Число элементов в «ColorMap» контролируется числом битов в элементе изображения и числом образцов, которые составляют элемент изображе- ния. В случае 4 бит на образец «ColorMap» должна содержать 16 элемен- тов. Бели используется 8 бит на образец, «ColorMap» будет содержать 256 элементов. Эта программа является сложной, поскольку режим VGA с од- новременным выводом 16 цветов использует палитру, которая придает кос- венный характер действительному числу цветового регистра. Режим VGA с выводом 256 цветов не использует палитру (подробности даны в гл. 1). Так или иначе массивы «RedColorMap», «GreenColorMap» и «BlueColorMap» загружаются значениями из цветовых регистров VGA. Они станут струк- турой «ColorMap» в файле TIFF. (Примечание: Значения цветовых реги- стров VGA находятся в диапазоне от 0 до 63, а значения «ColorMap» от О до 65535. Поэтому перед помещением в «ColorMap» значения, считывае- мые из цветовых регистров VGA, умножаются на 1024.) Бели «ColorMap» построена, для нее формируется элемент каталога. И наконец, растровые данные изображения считываются с экрана и пере- даются функции библиотеки TIFF «TIFFWriteScanline» для уплотнения и записи. То, каким образом информация считывается с экрана и передает- ся функции TIFF, определяется числом битов, которые используются для каждого элемента изображения, и режимом работы адаптера VGA. Бели кодируемое изображение имеет по горизонтали 640 элементов, програм- ма будет знать, что используется 4 бит на элемент, поэтому она упакует два значения четырехразрядного элемента в один байт. Такие байты нака- пливаются для одной строки выведенного на экран изображения и затем записываются на диск. Процесс повторяется для каждой строки изображе- ния. Аналогичный процесс используется и в том случае, если кодируемое изображение имеет по горизонтали 320 элементов. После того как данные изображения записываются в файл, вызывается функция «TIFFClose». Функция формирует все теги из внутренней струк- туры каталога (которые определяют растровое изображение) и записывает их на диск. Затем она закрывает выходной файл. После этого высвобожда- ется вся память, которая была размещена для этой функции, и управление передается вызывающей прикладной программе. 2. Считывание и вывод на экран VGA файла в формате TIFF» Прототип CompletionCode DisplayTIFFFile (char *FileName,unsigned Verbose); 16*
244 Глава 6. Форматы и функции графических файлов Таблица 6.6. (Продолжение) Где «FileName» — параметр, описанный выше. «Verbose» определяет, следует ли выводить на экран подробную инфор- мацию о структуре файла TIFF. Другими словами, если «Verbose» равен TRUE, вся информация о тегах из TIFF-файла будет выводиться на экран монитора перед выводом действительного изображения, а в случае FALSE будет выводиться только само изображение. Действие Действие этой функции проще, чем действие рассмотренной выше функ- ции. Сначала она открывает определенный TIFF-файл. Когда файл найден, проверяется флаг «Verbose», чтобы узнать, требуется ли подробная распе- чатка информации о TIFF-файле. Если требуется, для вывода на экран информации о тегах вызывается функция «TIFFPrintDirectory» из файла «print .с». Затем, чтобы проверить наличие обязательных тегов и найти ин- формацию о тегах, несколько раз вызывается функция «TIFFGetField». Если не найден какой-либо из обязательных тегов, действие функции пре- рывается после вывода на экран сообщения об ошибке. Если отсутствуют некоторые теги, которые имеют значение по умолчанию, выводится преду- преждающее сообщение, но действие продолжается. По параметрам изображения, найденным из TIFF-файла, можно устано- вить необходимый для вывода изображения режим VGA. После выбора видеорежима различные принимаемые по умолчанию палитры VGA загру- жаются только в том случае, если в TIFF-файле не найдена структура «ColorMap». Если же структура «ColorMap» считывается из TIFF-файла, то палитра VGA затирается. После того как завершена вся подготовитель- ная работа, действительные растровые данные считываются из файла по полосам и представляются выводящей части этой функции по принципу строка за строкой. То, как выводятся на экран данные строки, зависит от числа битов, которые используются на каждый элемент изображения. Для обработки изображений с 1, 4 или 8 бит на элемент используется отдельный сегмент кода. После вывода изображения вся память, размещенная этой функцией осво- бождается и TIFF-файл закрывается. Управление передается вызывающей программе. Изображение будет оставаться на экране до тех пор пока его не затрет другое изображение или опять не будет выбран текстовой режим. Листинг 6.3. Библиотека функций TIFF Ниже приводится содержимое файла tiffintf.h: /дедедедедедедедедедедедедедедедедедедеде/ /♦ Header файл ♦/ /♦ Функции библиотеки формата TIFF ♦/ /♦ разработан в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 12/29/89 ♦/ /дедедедедедедедедедедедедедедедедедедеде/
Формат графических файлов TIFF 245 tifdef _TIFFIO_ tindude "tiffio.h" tendif /♦ полезные функции ♦/ tdefine howmany(x, y) (((x)+((y)-l))/(y)) tdefine roundup(x, y) ((((x)+((y)-l))/(y))*(y)) /♦ Определение битов ошибок для кода TIFF ♦/ tdefine NoError О tdefine EBadParms -1 tdefine EFileNotFound -2 tdefine EReadFileDir -3 tdefine ENotTIFFFile -4 tdefine ECorrupt -5 tdefine EWrtFileDir -6 tdefine EWrtScanLine -8 tdef ine EGr aphi cs -10 tdefine ENoMemory -11 tdefine EUnKnownCompression -13 tdefine EFilelOError -14 /♦ Интерфейсные процедуры библиотеки поддержки формата TIFF ♦/ TIFF «TIFFOpen(char «name, char «node); CompletionCode TIFFClose(TIFF «tif); CompletionCode TIFFReadScanLine(register TIFF *tif, char *buf, long row, unsigned sample); CompletionCode TIFFReadStrip(register TIFF «tif, long strip); void TIFFPrintDirectory(TIFF «tif, FILE «fd, unsigned showstrips, unsigned showresponsecurve, unsigned showcolormap); long TIFFScanLineSize(TIFF *tif); CompletionCode TIFFGetField(TIFF «tif, unsigned tag,...); CompletionCode TIFFSetField(TIFF «tif, unsigned tag,...); CompletionCode TIFFSetDirectory(register TIFF «tif,long n); CompletionCode TIFFWriteScan(TIFF *tif,char «buf, unsigned row, unsigned sample); CompletionCode WriteTIFFFile(char «FileName, unsigned BitsPerSample, unsigned SamplePerPixel, unsigned Image Width, unsigned ImageLength); CompletionCode DisplayTIFFFile(char «FileName,unsigned Verbose); Далее следует содержание файла tiff .с: /**************************♦♦♦♦♦♦♦♦*♦♦♦*♦/ /« Библиотека функций ♦/ /♦ поддержки графического формата TIFF ♦/ /♦ изображения ♦/ /♦ разработана в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /« Последние изменения внесены 12/29/89 ♦/ /****************************************/
246 Глава 6, Форматы и функции графических файлов Листинг 6.3. (Продолжение) •include <stdio.h> •include <stdlib.h> •include <process.h> •include <nen.h> •include <conio.h> •include <string.h> •include <graphics.h> •include ’’nisc.h” •include '’pcx.h” •include ’’vga.h” •include "tiffintf.h” /* Создание TIFF-файла из изображения» выведенного на экран VGA монитора. ♦/ CoupletionCode WriteTIFFFile(char ♦FileName, unsigned BitsPerSample, unsigned SanplePerPixel, unsigned Inage Width, unsigned ImageLength) TIFF ♦tif; char ♦RovBuffer = NULL; char ♦Message я register unsigned Col, Rov; unsigned ♦RedColorMap = NULL; unsigned ♦GreenColorMap = NULL; unsigned ♦BlueColorMap = NULL; unsigned Index, ColorReg, BytesPerRov, Sample; unsigned RedColor, GreenColor, BlueColor; unsigned BitPlane, Mask; long RovsPerStrip; struct palettetype Palette; /♦ Размещение цветовых массивов. Функция Calloc заодно очищает их. */ if((RedColorMap s (unsigned ♦) calloc(MAX256PALETTECOLORS, sizeof (unsigned))) == NULL) Message ж "No nenory for RedColorMap”; goto NoMen; if((GreenColorMap = (unsigned ♦) calloc(MAX256PALETTEC0L0RS, sizeof(unsigned))) ss NULL) Message » "No nenory for GreenColorMap”; goto NoMen; if((BlueColorMap » (unsigned ♦) calloc(MAX256PALETTECOLORS, sizeof(unsigned))) — NULL)
Формат графических файлов TIFF 247 { Message • "Но memory for BlueColorMap"; goto HoMem; } tit - TIFFOpen(FileName,"w"); if(!tif) { restorecrtmodeO; printf("Error opening output file:%s\n".FileName); return(EFilelOError); } /* установка постоянных тегов */ TIFFSetField(tif,TIFFTAG_SUBFILETYPE,OL); TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,(long)ImageWidth); TIFFSetField(tif.TIFFTAG.IMAGELENGTH,(long)ImageLength); TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,BitsPerSample); TIFFSetField(tif,TIFFTAG.COMPRESSION,COMP RESSION.PACKBITS); TIFFSetField(tif.TIFFTAG.PHOTOMETRIC,PHOTOMETRIC-PALETTE); TIFFSetField(tif,TIFFTAG_MAKE,"Craig’s Video Digitizer"); TIFFSetField(tif,TIFFTAG_ARTIST,"Craig A.Lindley"); TIFFSetField(tif,TIFFTAG_HOSTCOMPUTER,"IBM PC with VGA graphics adapter"); TIFFSetField(tif,TIFFTAG.SAMPLESPERPIXEL,SamplesPerPixel); TIFFSetField(tif,TIFFTAG.XRESOLUTION,72.0); TIFFSetField(tif,TIFFTAG.YRESOLUTIOI,72.0); if(SamplesPerPixel “ 1) TIFFSetField(tif.TIFFTAG.PLANARCONFIG.PLANARCONFIG.CONTIG); else TIFFSetField(t if,TIFFTAG.PLANARCONFIG,PLAHARCOHFIG.SEPARATE); TIFFSetField(tif,TIFFTAG.RESOLUTIONUNIT,RESUNIT.INCH); BytesPerRow ж (unsigned) TIFFScanlinesSize(tif); /* выделение памяти для одной строки дисплея ♦/ if((RowBuffer * malloc(BytesPerRow)) “ NULL) { Message ” "No memory for RowBuffer"; goto NoMem; } /* выделение полосы размером около 8К */ RowsPerStrip 8; /* (8L*1024L)/(long)BytesPerRow */ TIFFSetField(tif,TIFFTAG.ROVSPERSTRIP,RowsPerStrip); if((BitsPerSample ”1) II (BitsPerSample " 4)) /* считываем палитру адаптера VGA ♦/ getpalette(tPalette); for(lndex«0; Index < 16; Index++) {
248 Глава 6. Форматы и функции графических файлов Листинг в.З. (Продолжение) /* Даже изображения размером 320x200 16 цветов используют режим VGA 13Н без палитры ♦/ if (InageWidth == LRMAXCOLS) ColorReg = Index; else ColorReg я Palette.colors[Index]; /* считываем компоненты цветового регистра из адаптера VGA ♦/ GetAColorReg(ColorReg,&RedColor,AGreenColor,ABlueColor); /♦ Масштабируем значения компонентов цвета в диапазоне 0 - 255, как того требует спецификация TIFF. ♦/ RedColorMap [Index] я RedColor « 2; GreenColorMap [Index] я GreenColor « 2; BlueColorMap [Index] я BlueColor « 2; else for(lndex=0; Index < MAX256PALETTEC0L0RS; Index++) /♦ считываем компоненты цветового регистра из адаптера VGA */ GetAColorReg( Index ,ARedColor, AGreenColor ,ABlueColor) ; /♦ Масштабируем значения компонентов цвета в диапазоне 0 - 255, как того требует спецификация TIFF. ♦/ RedColorMap [Index] я RedColor « 2; GreenColorMap [Index] я GreenColor « 2; BlueColorMap [Index] = BlueColor « 2; TIFFSetField(tif,TIFFTAG.COLORMAP,RedColorMap,GreenColorMap,BlueColorMap); if (InageVidth == HRMAXCOLS) /* изображение 640x200 или 640x480 ♦/ if(SanplePerPixel яя 1) for(Row»0; Row < ImageLength; Row++) { for(Col»0; Col < HRMAXCOLS; Col+«2) Sanple = getpixel(Col,Row) « 4; RowBuffer[Col»l] я Sanple + getpixel(Col+l,Row) ; if(!TIFFVriteScanline(tif,RowBuffer,Row,0))
Формат графических файлов TIFF 249 return(EWrtScanline); } } else { /* четыре образца на точку ♦/ for(BitPlane - 0; BitPlane < SanplesPerPixel; BitPlane++) < Mask - 1 « BitPlane; for(Roe = 0; Roe < InageLength; Roe++) { nenset(RoeBuffer,0,BytesPerRoe); for(Co1-0; Col < HRMAXCOLS; Col++) if(getpixel(Col,Roe) t Mask) RoeBuffer[Col»3] |- 1 « (7 - (Col % BITSPERBYTE)); if(!TIFFVriteScanline(tif,RoeBuffer,Roe,BitPlane)) return(EWrtScanline); } } } else /♦ Изображение 320x200. Предполагается, что любые изображения такого формата используют режим 13Н. Поэтому для считывания точки с экрана следует использовать функцию GetPixel256(). ♦/ if(BitsPerSaaple ” 4) for(Roe-0; Roe < InageLength; Roe++) { for(Col-0; Col < LRMAXCOLS; Col+»2) { Sanple = GetPixel256(Col,Roe) « 4; RoeBuff er [Col»l] * Sanple + GetPixel256(Col+l,Roe) ; if(!TIFFWriteScanline(tif,RowBuffer,Roe,0)) return(EWrtScanline); else /♦ изображение 320x200, 8 бит на элемент «/ for(Roe«0; Roe < ImageLength; Roe++) { for(Col-0; Col < LRMAXCOLS; Col++) RoeBuffer[Col] - GetPixel256(Col,Roe); if(!TIFFWriteScanline(tif,RoeBuffer,Roe,0)) return(EWrtScanline);
250 Глава 6. Форматы и функции графических файлов Листинг 6.3. (Продолжение) TIFFClose(tif); free((char *) RovBuffer); free((char *) RedColorMap); free((char ♦) GreenColorMap); free((char *) BlueColorMap); return(NoError); NoMem: restorecrtmode(); printf("Error: %s\n",Message); if(RovBuffer) free((char ♦) RovBuffer); if(RedColorMap) free((char *) RedColorMap); if(GreenColorMap) free ((char *) GreenColorMap); if(BlueColorMap) free((char *) BlueColorMap); TIFFClose(tif); return (ENoMemory); /♦ Данная функция отображает TIFF-файл на экране VGA монитора. Она поддерживает только небольшую часть возможных вариаций формата TIFF. Ограниченность этой функции обусловлена особенностями адаптера VGA. ♦/ CompletionCode DisplayTIFFFile(char «FileName,unsigned Verbose) < TIFF «tif; unsigned long ImageVidth, ImageLength; unsigned Photometriclntrp, Index; unsigned «RedColorMap, «GreenColorMap,«BlueColorMap; register short PixelNum; register unsigned Col, Rov; unsigned MaxColors, LoadColorMap, RovByte, RovByteCnt; unsigned BitColor, BitPlane, LovResImage; unsigned long ScanlineBytes; BYTE «RovBuffer; register BYTE ♦ RovBufferPtr; char «Message » BYTE «Map - NULL; struct palettetype Palette; unsigned BitsPerSample « 1; /« устанавливаем значения по умолчанию «/ unsigned SamplesPerPixel ж 1; unsigned PlanarConfig - PLANARCONFIG.CONTIG; /* открываем заданный файл */ tif « TIFFOpen(FileName,"r"); if('tif)
Формат графических файлов TIFF 251 { restorecrtmodeO; printf (’’TIFF Error:File %s not found or directory format error”,FileName); return(EFileNotFound); } if(Verbose) printf (”\nTIFF file: %s\n\n”, FileName); TIFFPrintDirectory(tif, stdout, TRUE, TRUE, TRUE); getchO; if((RowBuffer « malloc((unsigned)TIFFScanlineSize(tif))) жж NULL) Message ж ”No memory for RowBuffer”; goto bad; 1 /♦ проверяем наличие правильного состава тегов */ if (»TIFFGetField(tif ,TIFFTAG.IMAGEWIDTH,AlmageWidth) ) Message » ’’ImageWidth tag missing”; goto bad; 1 ImageWidth ж MIN(ImageWidth,HRMAXCOLS) ; if (!TIFFGetField(tif, TIFFTAG.IMAGELENGTH, AlmageLength) ) Message » ’’ImageLength tag missing”; goto bad; ImageLength ж MIN(ImageLength,HRMAXROWS); if(!TIFFGetField(tif,TIFFTAG.BITSPERSAMPLE,ABitsPerSample)) printf (’’Warning BitsPerSample tag missing\n”); /♦ проверяем правильность значений */ if ((BitsPerSample 1) (BitsPerSample !» 4) At (BitsPerSample !» 8)) Message » ’’BitsPerSample not 1,4 or 8”; goto bad; if (!TIFFGetField(tif ,TIFFTAG.SAMPLESPERPIXEL,ASamplesPerPixel) ) printf (’’Warning SamplesPerPixel tag missing\n”); /♦ проверяем правильность значений ♦/ if((SamplesPerPixel !« 1) Aft (SamplesPerPixel 4)) Message = ’’SamplesPerPixel not 1 or 4”; goto bad;
252 Глава 6. Форматы и функции графических файлов Листинг 6.3. (Продолжение) if(!TIFFGetField(tif.TIFFTAG.PLANARCONFIG.APlanarConfig)) printf("Warning PlanarConfig tag missing\n"); if (! TIFFGetField(tif, TIFFTAG .PHOTOMETRIC .ftPhotometridntrp) ) Message a "Pho tone trie Interpret at ion tag missing"; goto bad; /♦ проверяем правильность значений ♦/ if ( (Photometric Int гр !» PHOTOMETRIC.MINISWHITE) ft* (Photometriclntrp »« PHOTOMETRIC.MINISBLACK) ftft (Photometriclntrp »= PHOTOMETRIC.PALETTE)) Message = "Cannot handle this Photometricinterpretation"; goto bad; /♦ Определяем максимальное число цветов, необходимых для вывода данного изображения. Производим преобразование цветов для черно-белых изображений, если ^последние определяют белый цвет как 0 (адаптер VGA считает 0 черным цветом). Переменная LoadColorMap имеет значение True, если изображение имеет тег COLORMAP. Присутствие этого тега потребует впоследствии загрузки палитры. ♦/ MaxColors » 1 « (BitsPerSample ♦ SamplesPerPixel); Map » (char *)malloc((MaxColors) ♦ sizeof (BYTE)); LoadColorMap ж FALSE; sw it ch (Pho t omet r ic Intrp ) case PHOTOMETRIC.MINISBLACK: for(lndex=0; Index < MaxColors; Index++) Map [Index] * Index; break; case PHOTOMETRIC.MINISWHITE: for(lndex=0; Index < MaxColors; Index++) Map [Index] » MaxColors - Index - 1; break; case PHOTOMETRIC.PALETTE: /♦ Проверяем файл на наличие тега COLORMAP. Если он присутствует, загружаем три указателя на данные цветовой карты. ♦/ if(!TIFFGetField(tif.TIFFTAG.COLORMAP,ftRedColorMap, fcGreenColorMap. ABlueColorMap)) Message я "ColorMap tag missing"; goto bad; 1
Формат графических файлов TIFF 253 LoadColorMap ” TRUE; break; default: Message » ’’Unsupported Photometric Interpretation"; goto bad; } InitGraphicsO; /* определяем необходимый режим работы адаптера VGA */ if (ImageVidth <» LRMAXCOLS) /♦ менее 320 точек */ ImageLength « MIN(ImageLength,LRMAXROVS) ; /♦ обрезаем до 200 точек*/ Set256ColorMode () ; /♦ режим VGA 13Н ♦/ LoadGray64Palette(); /* 64 уровня яркости по умолчанию */ } else ImageVidth я MIN(ImageVidth,HRMAXCOLS); /* обрезаем до 640 точек ♦/ if(ImageVidth <= LRMAXROVS) /♦ 640x200 ? ♦/ setgraphmode(VGALO); else if (ImageVidth <• MRMAXROVS) /* 640x350 ? */ setgraphmode(VGAMED); else /♦ 640x480 */ setgraphmode(VGAHI); LoadGrayl6Palette(); /♦ 16 уровней серого цвета */ if(LoadColorMap) /♦ Загружаем палитру изображения в цветовые регистры. При этом делим значения компонентов цвета на 4, поскольку спецификация TIFF определяет цвета в диапазоне 0 - 255, а адаптер VGA - в диапазоне 0 - 63. */ if(!LovResImage) Palette.size «.MaxColors; for(lndex»0; Index < MaxColors; Index++) if(!LovResImage) Palette.colors[Index] = Index; Set AColorReg( Index, RedColorMap [Index] » 2, GreenColorMap [Index] » 2, BlueColorMap[Index] » 2) ; if(!LovResImage) setallpalette(APalette); } svitch(BitsPerSample) case 1: if(SamplesPerPixel » 1)
254 Глава 6. Форматы и функции графических файлов Листинг 6.3. (Продолжение) { ScanlineBytes я MIN(TIFFScanlineSize(tif),InageWidth/(long)BITSPERBYTE); for(RowsO; Row < ImageLength; Row++) { if(‘TIFFReadScanline(tif, RovBuffer, Row, 0)) break; RowBufferPtr » RovBuffer; for(RowByteCntM); RowByteCnt < ScanlineBytes; RowByteCnt++) RowByte « ♦RowBufferPtr++; for(PixelNums7;PixelNum >= 0; PixelNum—) BitColor s (Map[((RowByte ft (1 « PixelNum)) !» 0)])? 15:0; putpixel (RowlfyteCnt^8+(7-Pixel№im) , Row, В it Color) ; else /♦ 4 образца на элемент изображения ♦/ for(Row=0; Row < ImageLength; Row++) if(!TIFFReadScanline(tif, RowBuffer, Row, 0)) break; for(Cols0; Col < ImageVidth; Col++) if (LoadColorMap 9Л (ImageVidth »= LRMAXCOLS)) PutPixel256(Col,Row, (RowBuffer[Col»3] ft (1 « (7 - Col % BITSPERBYTE)))? 1:0); else putpixel (Col, Row, (RowBuff er [Col» 3] ft (1 « (7 - Col % BITSPERBYTE)))? 1:0); for(BitPlane « 1; BitPlane < SamplesPerPixel; BitPlanes++) for(Row=0; Row < ImageLength; Row++) if(!TIFFReadScanline(tif, RovBuffer, Row, BitPlane)) break; for(Col»0 ; Col < ImageVidth; Col++) if(RowBuffer[Col»3] ft (1 « (7 - Col % BITSPERBYTE))) if (LoadColorMap ftft (ImageVidth == LRMAXCOLS)) PutPixel256( Col, Row, GetPixel256(Col,Row) I (1 « BitPlane)); else putpixel( Col, Row, getpixel(Col,Row) I (1 « BitPlane)); break; case 4: ScanlineBytes s MIN(TIFFScanlineSize(tif) , ImageVidth/2L) ;
Формат графических файлов TIFF 255 for(Row«0; Row < Inage Length; Row++) { if(!TIFFReadScanline(tif, RowBuffer, Row, 0)) break; RowBufferPtr = RowBuffer; for(RowByteCnt«0; RowByteCnt < ScanlineBytes; RowByteCnt++) { RowByte « «RowBufferPtr++; if (LoadColorMap ftft (InageWidth «» LRMAXCOLS)) { Put Pixel256 (RowByteCnt«2 ,Row, ( (RowByte»4) ft OxFF) ); PutPixel256(RowByteCnt*2+1,Row,(RowByte ft OxFF)); else { putpixel(RowByteCnt*2,Row,((RowByte»4) ft OxFF)); putpixel(RowByteCnt«2+1,Row,(RowByte ft OxFF)); break; case 8: ScanlineBytes = MIN(TIFFScanlineSize (tif ), InageWidth) ; for(Rowe0; Row < Inage Length; Row++) { if (!TIFFReadScanLine(tif, RowBuffer, Row, 0)) break; RowBufferPtr я RowBuffer; for (RowByteCnt»0; RowByteCnt < ScanlineBytes; RowByteCnt++) PutPixel256(RowByteCnt,Row,(unsigned)«RowBufferPtr++); break; if (Map) free((char «)Map); if (RowBuffer) free((char ♦)RowBuffer); TIFFClose(tif); return(TRUE); bad: restorerectnodeO; printf(’’Error: %s\n", Message); if (Map) free((char «)Map); if (RowBuffer) free((char ♦)RowBuffer); TIFFClose(tif); return (FALSE);
256 Глава 6. Форматы я функции графических файлов Демонстрационная просмотровая программа Действие двух функций «WriteTIFFFile» и «DisplayTIFFFile», описанных в табл. 6.6, соответствует их аналогам из библиотеки поддержки формата PCX, которые были описаны в начале этой главы. Для иллюстрации функций би- блиотек поддержки TIFF и PCX в прикладной программе предназначена следу- ющая демонстрационная программа. Ее цель — вывод на экран VGA монитора правильно созданных изображений в формате PCX или TIFF. Эта демонстраци- онная программа оказалась весьма полезной во время создания данной книги, поскольку она позволяет быстро выводить различные изображения на экран для эффективной идентификации. Программа также может входить в виде команды файла AUTOEXEC.BAT для вывода на экран изображения при каждом запус- ке компьютера. Исходный код программы приведен в листинге 6.4. Его действие становится легко понятным из приведенных комментариев. Листинг 6.4. Программа View Ниже следует содержание файла view.prj: graphics.lib ПРИМЕЧАНИЕ: следует использовать версию этого файла vgagraph.obj — для средней модели памяти, чтобы программа просмотра egavga.obj работала правильно ! рсх (misc.h pcx.h) vga (misc.h vga.h pcx.h) io (tiff.h tiffio.h) swab (tiff.h tiffio.h) dir (tiff.h tiffio.h) dumpmode (tiff .h tiff io.h) error (tiff .h tiffio.h) compress (tiff.h tiffio.h) ccitt (tiff.h tiffio.h) packbits (tiff.h tiffio.h) Izw (tiff.h tiffio.h) print (tiff .h tiffio.h) tiff (tiff .h tiffio.h tiffintf .h) view (pcx.h tiffintf .h) Ниже следует содержание файла view.с: /**********************************************/ /♦ Программа просмотра файла PCX/TIFFgram ♦/ /♦ написана на Turbo С 2.0 ♦/ /♦ автор ♦/ /* Craig A. Lindley ♦/ /♦ Использование: */ /* view [-v ?] имя_файла[.рсх | .tif] */ /♦ Версия: 2.0 Дата: 12/11/89 */ /**********************************************/ /* ПРИМЕЧАНИЕ: Эта программа должна быть скомпилирована с использованием средней модели памяти Turbo С из-за ее размера. */
Демонстрационная просмотровая программа 257 tindude <stdio.h> tindude <process.h> tindude <conio.h> tindude <dos.h> tindude <graphics.h> tindude <string.h> tindude <io.h> tindude "misc.h" tindude "pcx.h" tindude "tiffintf .h" tdefine MAXFILENAMELENGTH 30 /* максимальная поддерживаемая длина файла ♦/ extern struct PCX_File PCXData; /* current revision level */ unsigned Release = 2; unsigned Revision a 0; /♦ Эта функция выдает подсказку в случае ошибки оператора. Программа прерывается после вывода подсказки ♦/ void ShovHelpC void ) printf("\nViev usage: view [-v ?] f ilename[.pcx I .tif] <cr>\n’’); printf(" -v displays image file inf ormationXn"); printf (" ? or -? displays this help text\n"); printf (" filename is паше given to PCX or TIFF image file\n\n"); exit (EBadParms); } void main (unsigned argc, char *argvO) { unsigned Verbose a FALSE; unsigned FileNameCounter, Argindex, StrLength; char * ImageF ileName; char FileName [MAXFILENAMELENGTH]; char PCXFileName [MAXFILENAMELENGTH]; char TIFFFileName [MAXFILENAMELENGTH]; clrscrO; print ("View - PCX or TIFF Image File Display Program\n"); print(" Version: 7,d.7.d by Craig A. LindleyXnXn", Release, Revision); /♦ обработка всех параметров командной строки */ FileNameCounter = 0; /♦ счетчик имен файлов ♦/ for (Argindex « 1; Argindex < argc; Arglndex++) { if (*argv[Argindex] ! » /* если не ключ командной строки */ { /* значит, имя файла */ 17-3
258 Глава 6. Форматы к функции графических файлов Листинг 6.4. (Продолжение) if (*argv [Argindex] == ’ ? ’) /♦ требуется помощь ? ♦/ ShowHelpO; if (FileNaneCounter >1) /* разрешено только одно имя */ ShowHelpO; /♦ если больше - выход по ошибке ♦/ Image File Name = argv [Argindex] ; /♦ записываем имя файла */ Filenamecount er++; /* продолжаем поиск ошибки */ 1 else /* ключ командной строки */ { switch (♦ (argv [Argindex]+1)) /* обработка командной строки */ { case ’v’: case ’V’: Verbose = TRUE; break; case ’?’: ShowHelpO; break; default: printf (’’Error - invalig cmd line switch encountered\n”); ShowHelpO; if (FileNaneCounter ! = 1) { print (’’Error: a single PCX or TIFF filename must be specif ied\n"); ShowHelpO; printf (’’Press any key to terminate display\n\n\n”) ; delay(1000); /* Проверка того, какой тип файла выводить на экран. Например, для добавления расширения имени файла, если оно не задано пользователем. */ strupr(ImageFileName) ; /♦ Преобразуем к верхнему регистру */ if (strstr(ImageFileName, ".PCX")) /* имеет расширение .PCX ? */ DisplayPCXFile(ImageFileName,Verbose); /* отображение файла PCX */ else if (strstr(ImagaFileName,".TIF")) /* имеет расширение .TIF ? */ DisplayTIFFFile(ImageFileName,Verbose); /♦ отображение файла TIF */ else { strcpy(FileName,""); /♦ очищаем память ♦/ /♦ находим длину имени файла без расширения */ StrLength = strcspn (ImageFileName,"."); if (StrLength == 0) /♦ расширение не задано */
Заключение 259 strncat(FileName,ImageFileName,MAXFILENAMELENGTH); /* полностью копируем имя файла */ else /* расширение задано */ strncat(FileName, ImageFileName, StrLength) ; /* копируем только имя файла */ /♦ Копируем имя обрабатываемого файла во все области хранения и составляем соответствующую строку. ♦/ stгсру(PCXFileName,FileName); strcat(PCXFileName,".PCX”); strcpy(TIFFFileName,FileName); strcat(TIFFFileName,”.TIF”); /* Определяем, существует ли файл с данными расширениями. Если да, то отображаем соответствующей функцией. */ if (access(PCXFileName,0) == 0) /♦ существует ли файл PCX ? */ DisplayPCXFile(PCXFileName,Verbose); /* да, отображаем*/ else if (access(TIFFFilename,O) «« 0) /* существует ли файл TIF ? */ DisplayTI FFFile(TIFFIleName, Verbose); /* да, отображаем*/ else print (’’Neither file %s nor 7«s found\n”, PCVXFileName,TIFFFileName); exit(EFileNotFound); } getchar(); restorecrtmode(); closegraphО; } Пожалуйста, уделите должное внимание файлу проекта view.prj и файлу с исходным кодом. Он иллюстрирует то, как любая прикладная программа долж- на взаимодействовать с библиотеками поддержки форматов PCX и TIFF. От- метьте также использование средней модели памяти в Turbo С, поскольку раз- мер модуля кода программы превышает 128К байт. Интересно, что для адапта- ции view.exe к средней модели памяти требуется изменить только один файл — vgagraph.asm. Эти изменения сводятся к вызову функций с адресацией типа FAR и уточнению смещений значения, о чем мы уже говорили в гл. 1. В общей слож- ности эта демонстрационная программа требует около 20000 строк кода на язы- ке Си. Заключение Эта глава оказалась достаточно длинной и вместила большое количество важной информации. При ее чтении вы должны были получить представление о структу- ре графических форматов, в особенности форматов PCX и TIFF. Теперь вы сами можете оценить ту работу, которую приходится выполнять по полной поддержке 17*
260 Глава б. Форматы и функции графических файлов даже одного графического формата. С помощью программного кода, рассмотрен- ного в главе (около 20000 строк на Си), самые разные изображения могут быть записаны в стандартных графических форматах, которые понимаются другими прикладными программами. Вы также получили возможность считывать файлы изображений, созданные другими программами. Наконец, вы имеете все компо- ненты, необходимые для построения программы преобразования, которая могла бы, например, считывать файл в формате PCX, а записывать — в формате TIFF. Программа view.exe продемонстрировала, как весь представленный в главе программный код может быть объединен в единое приложение. Эта программа также является неплохой самостоятельной утилитой для чтения изображений в форматах PCX и TIFF, а также для отображения информации об их структуре. В ч. II книги программа view.exe будет широко использоваться для просмотра результатов действия алгоритмов обработки изображения. Теперь, после того как мы научились сохранять изображения, полученные от видеопреобразователя, попробуем получить их печатные копии. Эти вопросы рассмотрены в гл. 7.
Глава 7 Создание печатных копий изображений В главе рассмотрены следующие вопросы: • Различные методы создания печатных копий изображений. • Печать изображения в газетах и журналах. • Пороговое отсекание, полутонирование и матрицирование. • Использование функции BIOS для доступа к принтеру. • Печать цветных и черно-белых изображений. Введение В предыдущих главах мы показали, как видеоизображение можно получить, со- хранить в виде файла графического формата и отобразить на экране монитора. В данной главе описаны различные приемы создания печатной копии предвари- тельно полученного изображения. Кроме того, мы обсудим некоторые техноло- гии, используемые для создания иллюстраций в полиграфии. Методы фотографического копирования На самом деле наиболее высококачественным и наименее дорогостоящим мето- дом получения твердой копии компьютерного изображения является фотогра- фия. Нужное изображение выводится на экран высококачественного монитора и фотографируется. Все иллюстрации в ч. II данной книги получены таким спо- собом. При этом, естественно, используется стандартная технология обработки фотографических материалов (проявление, закрепление и т. д.). Возможно, вести разговор о фотографии как методе получения твердой ко- пии в наш век компьютеров и лазерных принтеров несколько странно. К сожа- лению, далеко не все имеют доступ к высококачественным (и дорогим) прин- терам. Принтеры, способные производить изображения фотографического каче- ства, только что появились, и вряд ли будут широко доступны в ближайшие несколько лет. До тех пор пока не появится небольшой по размерам цветной принтер фотографического качества стоимостью не более нескольких сотен дол- ларов, нам вряд ли стоит отказываться от фотографического метода. Метод фотографии относительно прост. Он состоит из следующих элементов: 1. Снимок производится в темной комнате с расстояния не менее 3 м от мо- нитора. 2. Используются увеличительные линзы. Они позволят сгладить кривизну экрана монитора. В результате, фотография становится более плоской. 3. Уменьшается яркость и контрастность монитора. Яркие цвета и высокая интенсивность света приведут к искажению фотографии.
262 Глава 7. Создание печатных копий изображений 4. Выдержка фотокамеры должна находиться в интервале от 1/8 до 1/2 с. При выдержке 1/4 с начните работу с диафрагмы 1/8. Не следует исполь- зовать выдержку менее 1/15 с, поскольку это приблизительно равно вре- мени обновления изображения на экране. Слишком короткая выдержка даст изображение, на котором будет видна чересстрочная картина. Нельзя перемещать камеру или монитор во время экспозиции. Любое движение при таких выдержках разрушит качество получаемого снимка. Для достижения наилучших результатов следует провести серию снимков с различными значениями диафрагмы. Это, конечно, приведет к большому рас- ходу пленки, но зато позволит получить оптимальное качество снимка для ка- ждого изображения. Следуя приведенным инструкциям, вы сможете получить высококачественные фотографии как полутоновых, так и цветных изображений. Методы получения печатных копий на принтере Перед тем как заняться получением твердых копий изображения на принтере, ознакомимся с методами получения иллюстраций в книгах, газетах и журналах. Эти методы можно объединить общим термином «полутонирование» (halftoning). При полутонировании изображение представляется на бумаге в виде набора то- чек. Такие точки можно увидеть, если рассмотреть газету под увеличительным стеклом. Каждый элемент изображения на иллюстрации представляет собой бе- лый квадрат, содержащий черную точку. Чем больше размер точки, тем темнее будет данный элемент. Другими словами, площадь черной точки пропорциональ- на черноте элемента. Для создания темных областей на изображении использу- ются группы элементов, у которых размер точки почти максимален. Среднее значение серого цвета может быть сымитировано элементами изображения, у которых черная точка занимает примерно 50 процентов площади. Светлые обла- сти состоят из элементов с малыми размерами черных точек (рис. 7.1). Метод полутонирования работает потому, что человеческий глаз и мозг прово- дят пространственное объединение изображения. Глаз объединяет мелкие дета- ли изображения, наблюдаемого с некоторого расстояния, и регистрирует только усредненную интенсивность. Переменная интенсивность черных точек внутри бе- лой картинки моделирует полутоновое изображение. Глаз видит изображение, в котором цвета меняются непрерывно, от черного до белого. Общее число различ- ных интенсивностей определяется имеющимся количеством различных размеров черных точек. Большинство газет создает иллюстрации с разрешением от 60 до 80 точек на дюйм (DPI). Книги обычно печатают с разрешением 150 DPI. К сожалению, принтеры для персональных компьютеров не обладают воз- можностью печатать черные точки переменного размера. Поэтому для них ис- пользуются иные методы. Наиболее важными из них являются пороговое отсе- кание, матрицирование и псевдотонирование. Каждый из этих методов имеет свои достоинства и недостатки, которые мы обсудим ниже. Отметим, что методы порогового отсекания, матрицирования и псевдотониро- вания мы будем обсуждать только в контексте вывода изображений на принтер. Эти методы, однако, также можно применять и для других выходных устройств, например мониторов.
Методы получения печатных копий на принтере 263 Каждый элемент изображения может иметь любой уровень интенсивности из представленных ниже Рис. 7.1. Создание полутонового изображения. Здесь показаны семь различных уровней интенсивности. Чем больше размер черной точки, тем темнее элемент изображения. Механизм пространственной интеграции, при- сущий глазу и мозгу человека, позволяет работать такой системе воспроизведения изо- бражений. Пороговое отсекание Пороговое отсекание не моделирует полутонирование. Оно, скорее, представля- ет собой грубый метод вывода на принтер полутоновых изображений. Это метод включен в обсуждение для полноты картины. Результаты применения порогово- го отсекания настолько плохи, что мы не приводим здесь даже демонстрационной программы реализации этого метода. Тестовое изображение (рис. 7.2) показыва- ет часть изображения, обработанного методом порогового отсекания, чтобы дать представление о его качестве. Как видно из этого рисунка, все детали изображения утеряны. Рисунок мож- но понять только в том случае, когда вы знаете, что именно на нем изображено. Для получения этого рисунка исходное изображение сканировалось поточечно. Интенсивность каждой точки сравнивалась с заранее заданным значением (по- рогом) для решения вопроса о включении этой точки в изображение, выводимое на принтер. Все точки с интенсивностью, превышающей значение порога, отобра- жаются на бумаге (печатаются принтером) в виде черных точек. Все остальные точки как бы удаляются из изображения и заменяются белыми. Правильный вы- бор порогового значения может позволить получить весьма интересные изобра-
264 Глава, 7, Создание печатных копий изображений Рис. 7.2. Изображение, полученное методом порогового отсекания. жения. В большинстве случаев, однако, печатные копии такого рода не находят применения. Пороговое отсекание может быть использовано в качестве художе- ственного приема. Но оно неприменимо в качестве метода получения печатной копии общего назначения. Итак, пороговое отсекание преобразует полутоновое или цветное изображение в двухуровневую черно-белую печатную копию. Ре- зультаты использования этого метода обычно не удовлетворяют общепринятым стандартам. Матрицирование Метод порогового отсекания непригоден потому, что он полностью разрушает полутоновую природу исходного изображения при печати. Матрицирование, ко- торое имитирует полутонирование, позволяет сохранить на печатной копии не- которую (если не всю) информацию об уровнях яркости даже при печати на двухуровневом устройстве типа принтера. Это достигается путем снижения раз- решающей способности при печати различных уровней интенсивности. Матри- цирование наиболее эффективно в тех случаях, когда устройство вывода име- ет более высокое разрешение, чем исходное изображение. В этом случае не на- блюдается ухудшения качества и матрицирование приводит к получению очень хорошей копии исходного изображения. Если же устройство вывода не имеет достаточной разрешающей способности, то на печатной копии мы получим сни- жение разрешения в качестве платы за получение различных уровней яркости. Причину этого мы увидим из последующего обсуждения. В любом случае, чем выше разрешающая способность принтера, тем лучше. Более высокая разреша- ющая способность позволяет более гибко подбирать алгоритмы для получения печатной копии.
Методы получения печатных копий на принтере 265 Матрицирование использует группу элементов изображения на устройстве вывода (в нашем случае на принтере) для представления одного элемента ис- ходного изображения. Число «установленных» элементов внутри группы (т. е. тех элементов, которые будут напечатаны) определяет относительную яркость или затемнение всей группы. Элементы изображения внутри группы имитируют черную точку внутри полутонового элемента, размер которого определяет значе- ние интенсивности. Иными словами, число установленных элементов определяет интенсивность группы. Группы элементов обычно имеют вид квадратных матриц. Группа размером пхп может воспроизвести n2 + 1 различных уровней интенсивности. Метод ма- трицирования проиллюстрирован рис. 7.3. Группа размером 4x4, используемая в демонстрационной программе, описываемой в этой главе, может воспроизво- дить 17 уровней интенсивности на устройстве вывода. Это более чем достаточно для 16-цветовых изображений, получаемых с помощью видеопреобразователя. Матрица, показанная на рис. 7.3, предложена Риландером и называется рекур- сивной матрицей Риландера. Для визуализации процесса матрицирования представьте себе, что каждый элемент исходного изображения превращается в один «большой» элемент на устройстве вывода. Для группы размером 4x4 элемент выходного изображе- ния в действительности будет состоять из 16 элементов. Такое преобразование приведет к уменьшению эффективной разрешающей способности устройства вы- вода в четыре раза в каждом направлении. Например, если устройство вывода имеет разрешение 640 х 480 точек, эффективное разрешение при использовании матрицирования станет равно 160 х 120 точек (но при этом устройство получит способность воспроизводить 17 уровней интенсивности). Таким образом, воспро- изведение различных уровней интенсивности достигается за счет уменьшения разрешения. Если на печатной копии требуется получить больше уровней интенсивности, следует использовать матрицу большего размера. Компромисс между разрешени- ем печатной копии и числом уровней интенсивности определяется человеческим восприятием. Иными словами, матрицирование становится неприемлемым тогда, когда группы элементов изображения становятся настолько большими, что это приводит к искажению выходного изображения. Матрицы элементов изображения, которые воспроизводят различные уровни интенсивности, должны подбираться самым тщательным образом. Неправильно подобранные матрицы приведут к разрушению строения изображения, особенно в области с постоянной интенсивностью. Обычно матрицы представляют собой возрастающие последовательности, генерируемые рекурсивно. Для получения хороших результатов очень важно, чтобы каждый элемент в группе, который является установленным при некотором значении уровня интенсивности, оста- вался установленным при всех более высоких значениях интенсивности. Мате- матически это можно сформулировать следующим образом: каждый элемент, установленный на уровне J, должен быть установленным на всех уровнях K>J. Если это условие не соблюдается, то выходная печатная копия изображения будет неоптимальной. Изображения, обработанные по методу матрицирования, имеют обычно не- сколько грубую, но постоянную текстуру. Матрицирование генерирует изобра- жения, имеющие уменьшенное содержание высокочастотной составляющей по сравнению с псевдотонированными изображениями. Поэтому такие изображения
266 Глава, 7. Создание печатных копий изображений 00 X 08 X 08 X X 0А 00 00 00 00 00 00 X 02 X 02 00 00 00 00 0% 6 3% 12.5% 18.8% Прнмечаине 2 X X 0А X X 0А X X 0А X X 0А 00 X 08 X 08 X X 0А X X 0А X X 0А X X 0А X X 0А 00 00 X 02 X 02 25% 31.3% 37.5% 43.8% X X 0А XXX 0Е XXX 0Е X X X X 0F X X 0А X X 0А X X 0А X X 0А X X 0А X X 0А X XX ОВ X XX ОВ X X 0А X X 0А X X 0А X X 0А 50% 56 3% 62.5% 68.8% X X X X 0F X X X X 0F X X X X 0F X X X X 0F X X X X OF X X 0А XXX 0Е XXX 0Е X X X X OF X X X X OF X X X X 0F X X X X 0F X X X X 0F X X X X OF X X X X OF X X 0А X X 0А X XX ОВ X XX ОВ X X X X OF 75% 1 81.3% 87.5% 93.8% 100% I а Матрицы 4x4 Рекурсивные матрицы Риландсра Матричное обозначение О 8 2 10 4 12 6 14 3 11 1 9 7 15 5 13 б Рис. 7.3. Матрица Риландера 4x4. Примечания 1. При выводе матрицы (б) элементы, превышающие значения интенсивности исходного элемента изображения, устанавливаются. Все остальные элементы матрицы остаются неустановленными. 2. Знак X обозначает установленный элемент. Пустые места соответствуют неустано- вленным элементам. 3. Матрица размером 4x4 воспроизводит н2 4-1 или 17 различных уровней интенсив- ности.
Методы получения печатных копий на принтере 267 более точно отображаются на мониторах компьютеров. При выводе на принтер, однако, содержание высокочастотной составляющей не играет такой роли. Псевдотонирование Метод псевдотонирования применяется для получения печатных копий полу- тоновых изображений в том случае, когда разрешающая способность устрой- ства вывода равна разрешению исходного изображения, или когда уменьшение разрешающей способности устройства вывода недопустимо. Псевдотонированные изображения обычно имеют более гладкую текстуру по сравнению с матрициро- ванными. Они также дают больше высокочастотных компонентов, которые, как уже говорилось, могут сильно «докучать» компьютерным мониторам. Методы упорядоченного псевдотонирования были разработаны Б. Е. Байером. Методы псевдотонирования почти идентичны методам матрицирования, опи- санным выше. Псевдотонирование, подобно матрицированию, использует матри- цу для преобразования исходного изображения. Более того, содержимое этой матрицы практически идентично содержанию матрицы в случае матрицирова- ния. Ниже обе матрицы приведены для сравнения. Матрица Ридандера Упорядоченная матрица псевдотонирования Байера 0 8 2 10 0 8 2 10 4 12 6 14 12 4 14 6 3 11 1 9 3 11 1 9 7 15 5 13 15 7 13 5 Различие между этими двумя методами заключается в том, как матрица обрабатывает изображение. Для понимания того, как работает матрица псев- дотонирования, представьте себе, что все исходное изображение покрыто такой матрицей. Каждый элемент исходного изображения соответствует одному эле- менту матрицы псевдотонирования. Элемент выходного изображения, соответ- ствующий определенному элементу исходного изображения, будет установлен в том случае, когда значение интенсивности элемента превышает значение связан- ного с ним элемента матрицы псевдотонирования. В противном случае элемент выходного изображения будет неустановленным. Псевдокод подобной процеду- ры выглядит следующим образом (элемент изображения Р имеет координаты «XCoord» и «YCoord»): for матрица псевдотонирования размером n х п i я XCoord nod n j s YCoord nod n if интенсивность элемента P > Dij then выходной элемент установлен else выходной элемент не установлен
268 Глава 7. Создание печатных копий изображений Исходное изображение Часть исходного изображения соответствующий элемент матрицы псевдотонирования, включает отображение элемента выходного Примечание 2 □□□□□□ Выходное изображение Рис. 7.4. Упорядоченное псевдотонирование изображения. Примечания 1. Все точки исходного изображения промасштабированы в интервале 0... п2. Для п = 4 этот интервал составляет 0... 16. 2. Черные точки указывают на установленные элементы выходного изображения. 3. Исходное и выходное изображения имеют одинаковое разрешение. где D является матрицей псевдотонирования размером n х n, a i и j являются индексами. На рис. 7.4 показано применение матриц размером 4 х 4 к небольшому участку изображения. Подобно матрицированию, матрицы псевдотонирования больших размеров могут быть использованы для получения дополнительных значений интенсивно-
Программа печати экрана 269 сти, если это необходимо. Общее число различных уровней интенсивности опять равно п2 +1. При этом применение слишком больших матриц псевдотонирования приводит к тому, что интенсивность выходного изображения больше не содер- жит информации об исходном изображении. Матрица размером 4x4 является хорошим компромиссом между числом уровней интенсивности и разрешением изображения. Программа печати экрана Программа, представленная в листинге 7.1, считывает файл формата PCX с дис- ка, выводит изображение на экран и затем печатает его на матричном принтере, совместимом с принтером IBM. Программа работает только с изображениями 320 х 200 256 цветов, 640 х 200 16 цветов и 640 х 480 16 цветов. Неудивительно, что это именно те разрешения, которые поддерживаются видеопреобразовате- лем, описанным в предыдущих главах. Однако наша программа не ограничива- ется только печатью изображений от видеопреобразователя. Любое изображение в формате PCX подходящего разрешения может быть напечатано с ее помощью. Все печатаемые изображения используют матрицу псевдотонирования размером 4x4 или матрицирование того же размера. Это дает возможность воспроизводить на печатной копии 17 различных уровней интенсивности. Прежде, чем продолжить обсуждение, отметим, что создание программы пе- чати общего назначения является крайне сложной задачей. При аккуратном вы- воде изображения на принтер возникают следующие проблемы: 1. Проблемы соотношения геометрических размеров, т. е. проблемы вывода изображения на принтер в тех же пропорциях, в которых оно отобража- лось на мониторе. Детальное обсуждение проблемы коррекции соотноше- ния геометрических размеров проводится в гл. 12 этой книги. 2. Поддержка цветной печати на цветном принтере или преобразование цвет- ного изображения в черно-белое для обычного принтера. Это область при- менения методов порогового отсекания, матрицирования и псевдотониро- вания. 3. Разрешение изображения и поддержка различных цветовых схем. Если драйвер принтера предназначен для печати изображения с видеодисплея, он должен поддерживать все 19 возможных видеорежимов, определенных фирмой IBM для компьютеров серии PC/PS2. 4. Различия между принтерами разных моделей и производителей, т. е. тре- буется учитывать различные разрешения и управляющие команды для разных принтеров. Наша демонстрационная программа не ставит своей целью быть драйвером принтера общего пользования. Создание такой полноразмерной программы с не- избежностью усложнило и запутало бы представляемую в этой главе инфор- мацию. Демонстрационная программа, подобно программе «graphics.com», по- ставляемой вместе с MSDOS, поддерживает лишь небольшое число видеорежи- мов. Это вызвано большой сложностью поддержки всех возможных видеорежи- мов. Далее, вместо того, чтобы обеспечивать поддержку всех возможных типов
270 Глава 7. Создание печатных копий изображений Матрициро- ванные твердые копии Рис. 7.5. Печатные копии изображений.
Программа, печати экрана 271 принтеров, наша программа позволяет работать только с принтерами, доступ- ными большинству пользователей, — а именно с обычными черно-белыми 9- игольчатыми матричными принтерами. Хотя представленные методы, конечно, можно использовать на любых принтерах, включая цветные и лазерные. Лазер- ные принтеры, эмулирующие принтер типа «IBM Graphics Printer», могут непо- средственно использовать представленный код. Кроме отсутствия достаточной общности, другим важным компромиссом, сде- ланным во время разработки программы, является отсутствие коррекции соотно- шения геометрических размеров. Печатные копии, получаемые с помощью этой программы, в большинстве случаев имеют соотношения геометрических разме- ров, «близкие» к исходному изображению. В то же время, например, изобра- жения 320 х 200, напечатанные методом матрицирования, вообще не сохраняют соотношение геометрических размеров. Такие изображения лучше печатать по методу псевдотонирования. Методы коррекции соотношения геометрических раз- меров для видеомонитора, описанные в гл. 12, могут быть также использованы и для печати. Введение кода коррекции соотношения геометрических размеров в демонстрационную программу мы оставляем читателю. Демонстрационная программа использовалась для создания всех печатных копий изображений, представленных в этой главе (рис. 7.5). Эта программа со- стоит из файлов printer.h, print.c и print.prj, представленных в листинге 7.1. Эти файлы в их настоящем виде образуют самостоятельную программу. Прикладная программа, желающая использовать код печати графического экрана, должна его слегка модифицировать. Для этого следует удалить функцию «main()» и до- бавить необходимые параметры в вызов функции «PrtScreen». Кроме того, при- кладная программа должна использовать файл printer.h. Листинг 7.1. Демонстрационная программа печати экрана Ниже приводится содержимое файла print.prj: graphics.lib vgagraph.obj egavga.obj vga (misc.h vga.h) pcx (misc.h pcx.h) print (misc.h pcx.h vga.h printer.h) Далее следует текст файла printer .h: /****************************************/ /♦ Header файл ♦/ /♦ Программы печати изображения ♦/ /♦ разработан в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 11/29/89 ♦/ /*************«*********************«****/
272 Глава 7. Создание печатных копий изображений Листинг 7.1. (Продолжение) «ifndef .BYTE «define -BYTE typedef unsigned char BYTE «endif «define P0SPRINT «define MEGPRIMT TRUE FALSE /♦ все точки, значения которых превышают пороговые, печатаются черными ♦/ /♦ все точки, значения которых не превышают пороговые, печатаются белыми ♦/ /♦ Определения состояния принтера ♦/ «define PRTTIMEOUT 0x01 «define PRTBUSYBIT 0x80 /♦ старший значащий бит статуса принтера ♦/ «define PRIMTERIMT 0x17 /♦ номер прерывания принтера BIOS ♦/ «define PRTCHARCODE 0 /♦ код функции печати символа прерывания 17Н ♦/ «define IMITPRTCODE 1 /♦ код функции инициализации принтера прерывания 17Н ♦/ «define GETPRTSTATUSCODE 2 /* код функции получения статуса принтера прерывания 17Н ♦/ «define LPT1 0 /♦ коды выбора принтера для BIOS ♦/ «define LPT2 1 «define LPT3 2 «define PIXELSPERPASS 8 /♦ печатавшая головка принтера печатает 8 точек за один проход ♦/ typedef enun (LovResMode, MedResMode, HighResMode) PrinterModes; typedef enun (BayerMatrix, RylanderMatrix) MatrixType; /♦ Структура данных для хранения информации о структуре полутоновой точки ♦/ struct DotPattems { BYTE Rowl; BYTE Row2; BYTE Rov3; BYTE Rov4; }; /♦ Прототипы функций ♦/ Conplet ionCode Prt Screen (unsigned NegPos); Далее следует текст файла print.с: /******************************************************/ /♦ Программа вывода на экран и печати ♦/ /* .PCX файлов и изображений, выведенных ♦/ /♦ на экран VGA монитора. Печать осуществляется */ /♦ на матричном принтере, совместимом с IBM ♦/ /* Разработана в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли */
Программа печати экрана 273 /* Использование: */ /* print [-v -jbB/R -b(+/- 0-9) -n] filenane <сг> ♦/ /♦ Значения переключателей: ♦/ /♦ -v - вывод информации о параметрах файла .PCX */ /♦ -ж - выбор типа матрицы для псевдотонирования: ♦/ /♦ В - упорядоченная матрица Байера */ /♦ R - рекурсивная матрица Риландера ♦/ /* -Ъ - изменение яркости ♦/ /* -п - печать негативного изображения */ /* */ /* Версия: 1.0 */ /♦ Последние изменения внесены 11/29/89 ♦/ /*ф*ф*****************ф***ф**************ф**ф**********/ tindude <stdio.h> tindude <conio.h> tindude <dos.h> tindude <process.h> tindude <stdlib.h> tindude <string.h> tindude <graphics.h> tindude "nisc.h" tindude "pcx.h'1 tindude "vga.h" tindude "printer.h" tdefine MAXVGAINTVAL 6300.0 /♦ максимальная интенсивность адаптера VGA x 100 ♦/ tdefine MAXPRTINTENSITIES 17 /♦ 17 возможных уровней яркости для матрицы размером 4 на 4 ♦/ tdef ine MAXDENSITYINDEX (MAXPRTINTENSITIES-1) /♦ Информация о версии программы ♦/ static short ver = 1; static short rel = 0; /♦ некоторые переменные ♦/ static unsigned Verbose, PrintMode; static int Brightness; static unsigned MaxScreenRov, MaxScreenCol, Is256Colors; static unsigned DensityTbl[MAX256PALETTECOLORS]; static unsigned Nl, N2; static union REGS regs; static enun MatrixType Matrix; /♦ Ниже следуют массивы, которые определяют матрицы, используемые для моделирования полутонирования при выводе изображения на принтер. В обоих случаях используются матрицы размером 4 на 4. ♦/ /♦ Упорядоченная матрица псевдотонирования Байера ♦/ 18-3
274 Глава 7. Создание печатных копий: изображений Листинг 7.1. (Продолжение) BYTE BayerMatrix[4] [4] < o, 8, 2, 10, 12, 4, 14, 6, 3, 11» 1» 9, 15, 7, 13, 5}; /* Рекурсивная матрица Риландера метода матрицирования */ struct Dot Pat terns RylanderMatrix[MAXPRTINTENSITIES] » {OxOF, OxOF, OxOF, OxOF, /* 100.0% - все точки черные ♦/ OxOF, OxOF, OxOF, OxOB, /* 93.8% ♦/ OxOF, OxOE, OxOF, OxOB, /* 87.5% */ OxOF, OxOE, OxOF, OxOA, /* 81.3% */ OxOF, OxOA, OxOF, OxOA, /* 75.0% ♦/ OxOF, OxOA, OxOB, OxOA, /* 68.8% »/ OxOE, OxOA, OxOB, OxOA, /* 62.5% */ OxOE, OxOA, OxOA, OxOA, /* 56.3% »/ OxOA, OxOA, OxOA, OxOA, /* 50.0% »/ OxOA, OxOA, OxOA, 0x02, /♦ 43.8% ♦/ OxOA, 0x08, OxOA, 0x02, /* 37.5% */ OxOA, 0x08, OxOA, 0x00, /* 31.3% ♦/ OxOA, 0x00, OxOA, 0x00, /* 25.0% ♦/ OxOA, 0x00, 0x02, 0x00, /* 18.8% ♦/ 0x08, 0x00, 0x02, 0x00, /♦ 12.5% ♦/ 0x08, 0x00, 0x00, 0x00, /* 6.3% */ 0x00, 0x00, 0x00, 0x00}; /♦ 0.0% - все точки белые */ /* Управлявшие команды принтера типа IBM Proprinter */ BYTE *OneDirection = "\xlBU\x31"; /* ESC U 1 ♦/ BYTE .TeoDirection = "\xlBU\x30"; /* ESC U 0 »/ BYTE *LovRes « "\xlBK"; /* ESC К »/ BYTE *MedRes « "\xlBY"; /* ESC Y */ BYTE .HighRee “ "\xlBZ"; /♦ ESC Z ♦/ BYTE *TextLinePeed « "\xlB\x31"; /♦ ESC 1 - межстрочный интервал 7/72 »/ BYTE *GraphicLineFeed = "\xlB\x33\xl8"; /* ESC 3 24 - межстрочный интервал 24/216 ♦/ BYTE *CrLf = "\r\n"; /♦ возврат каретки и перевод строки ♦/ BYTE *DisAutoLf = "\xlB\x3S\x30"; /* ESC 5 0 - выключение автоматического перевода строки после возврата каретки */ /* Функции интерфейса принтера */ /♦ Данная функция инициализирует принтер */ void Prtlnit (unsigned PrtNun) { regs.h.ah « INITPRTCODE; regs.h.dx = PrtNun; int86(PRINTERINT, Aregs, Aregs); /♦ Данная функция считывает статус принтера */ BYTE PrtStatus (unsigned PrtNun)
Программа, печати экрана 275 { regs. h. ah = GETPRTSTATUSCODE; regs.h.dx B PrtNum; int86(PRINTERINT, Aregs, ftregs); return (regs .h. ah) ; /♦ Данная функция печатает символ на заданном принтере */ BYTE Prt Char (unsigned PrtNum, BYTE Character) while (• (Prt St atus (PrtNum) ft PRTBUSYBIT) ) ; /♦ ожидание готовности ♦/ regs.h.ah e PRTCHARCODE; /♦ команда печати символа ♦/ regs. h. al = Character; regs.h.dx e PrtNum; /♦ выбранный принтер ♦/ int86(PRINTERINT, ftregs, ftregs); return(regs.h.ah) ; /♦ возвращает статус операции ♦/ /♦ Данная функция печатает строку символов на заданном принтере ♦/ BYTE Prt String (unsigned PrtNum, BYTE «String) < BYTE PrtError; PrtError » FALSE; while ( («String ! = NULL) At (! PrtError) ) /♦ выполняем, пока не получим значение null или ошибку ♦/ PrtError « ((PrtChar(PrtNum, *String++) A PRTTIMEOUT) “ 1); return(PrtError); 1 /♦ Данная функция создает таблицу DensityTbl, которая содержит индексы в массиве DotDensities. Этот массив используется для представления цвета элемента изображения в шале яркости при печати. Яркость печатаемой точки вычисляется по значении цветового регистра. Расчет производится по следующей формуле: Интенсивность в Красная компонента ♦ 30*/, + Зеленая компонента ♦ 59% + Синяя компонента ♦ 11%, что соответствует чувствительности человеческого глаза к различным составляющим цвета. Эта формула взята автором из технического руковод- ства фирмы IBM. Для адаптера VGA значения цветовых регистров являются переустанавливаемыми. Это означает, что компоненты регистра RGB должны быть считаны из адаптера до выполнения расчета яркости. Каждая компонента цвета находится в интервале от 0 до 63. Это значение будет использовано для масштабирования значений яркости в диапазоне от 0 до 16, как того требует массив DensityTbl. ♦/ void InitDensityTbl(void) unsigned ColorEntry, ColorNum; unsigned Intensity; struct palettetype palette; 18*
276 Глава 7. Создание печатных копий изображений Листинг 7.1. (Продолжение) /* Данная процедура создает DensityTbl на основе значений интенсивности цветов, используемых в палитре изображения в формате PCX. Метод расчета интенсивности описан выше. */ getpalette(ftpalette); /♦ получаем текущую палитру */ if (!Is256Colors) /* режимы с 16 цветами */ { /* для каждого элемента палитры ♦/ for(ColorEntry=0; ColorEntry<palette.size; ColorEntry++) { /♦ Поскольку значения цветовых регистров адаптера VGA могут изменяться, компоненты цвета каждого элемента палитры должны быть считаны перед началом расчета интенсивности. ♦/ regs.h.ah = 0x10; /♦ получаем значения компонентов цвета ♦/ regs.h.al s 0x15; /* для цветового регистра. */ regs.h.bx = ColorNum; int86(VIDEO, ftregs, ftregs); /♦ Если палитра уже содержит значения яркостей, а не цвета, интенсивность определяется только одной компонентой (красной). Значение интенсивности нужно промасштабировать. ♦/ if ((regs.h.dh == regs.h.ch) ftft (regs.h.ch. ss regs.h.d)) Intensity = regs.h.dh ♦ 100; else { /♦ рассчитываем значение интенсивности ♦/ Intensity = ((unsigned) regs.h.dh * 30); /♦ для красной */ Intensity +« ((unsigned) regs.h.ch ♦ 59); /♦ для зеленой ♦/ Intensity += ((unsigned) regs.h.cl * 11); /* для синей */ /♦ масштабируем и запоминаем значение интенсивности */ DensityTbl[ColorEntry] = (unsigned) (((((double) Intensity ♦ (double) MAXDENSITYINDEX) / MAXVGAINTVAL) + 0.5)); else /* 256 цветов ♦/ { /♦ для каждого цветового регистра */ for(ColorNuB»0; ColorNwa<MAX256PALETTEC0L0RS; ColorNua++) { /♦ в режиме 256 цветов палитра не используется ♦/ regs.h.ah ш 0x10; /♦ получаем значения компонентов цвета ♦/ regs.h.al s 0x15; /* для цветового регистра. ♦/ regs.h.bx = ColorNun; int86(VIDEO, ftregs, ftregs);
Программа печати экрана 277 /* Если палитра уже содержит значения яркостей, а не цвета, интенсивность определяется только одной компонентой (красной). Значение интенсивности нужно пр о масштабировать. ♦/ if ((regs.h.dh яя regs.h.ch) (regs.h.ch. яя regs.h.cl)) Intensity я regs.h.dh * 100; else { /* рассчитываем значение интенсивности */ Intensity я ((unsigned) regs.h.dh * 30); /* для красной */ Intensity +s ((unsigned) regs.h.ch * 59); /* для зеленой ♦/ Intensity +я ((unsigned) regs.h.cl *11); /♦ для синей ♦/ /♦ масштабируем и запоминаем значение интенсивности */ DensityTbl [ColorEntry] я (unsigned) (((((double) Intensity* (double) MAXDEMSITYINDEX) I MAXVGAINTVAL) + 0.5)); 1 } /♦ Данная функция возвращает значение элемента изображения на экране. Она выполняется по-разному для режимов с 16 и 256 цветами. ♦/ unsigned GetАРixel(unsigned Col, unsigned Row) { if (Is256Colors) retum(GetPixel256 (Col, Row) ) ; else /* 16 цветов */ retum(getpixel(Col, Roev)); void DitherPrintCol(unsigned PrintMode, PrinterModes PrtMode, unsigned Col) { register unsigned CurrentRov, MatrixRov; register BYTE Intensity, PrtByte; switch (PrtMode) /♦ устанавливаем на принтере */ { /* соответствующий графический режим. */ case HighResMode:{ PrtString(LPTl, HighRes); break; case MedResMode:{ PrtString(LPTl, MedRes); break; } case LovResMode:{ PrtString(LPT1, LovRes); break; 1 1
278 Глава 7. Создание печатных копий изображений Листинг 7.1. (Продолжение) /* сообщаем принтеру количество передаваемых байтов */ PrtChar(LPTl, N1); PrtChar(LPTl, N2); for(CurrentRow=0; CurrentRow<MaxScreenRow; CurrentRow++) { PrtByte » 0; MatrixRow » CurrentRow % 4; Intensity = DensityTbl[GetAPixel(Col, Current Row)] + Brightness; if (Intensity > BayerMat [matrixRow] [3] ) PrtByte Iя 128; Intensity я DensityTbl[GetAPixel(Col-l, CurrentRow)] + Brightness; if (Intensity > BayerMat [matrixRow] [2] ) PrtByte I» 64; Intensity = DensityTbl[GetAPixel(Col-2, Current Row)] + Brightness; if (Intensity > BayerMat [matrixRow] [1] ) PrtByte 1= 32; Intensity = DensityTbl [GetAPixel (Col-3, Current Row)] 4* Brightness; if (Intensity > BayerMat [matrixRow] [0] ) PrtByte |= 16; Intensity = DensityTbl[GetAPixel(Col-4, CurrentRow)] + Brightness; if (Intensity > BayerMat [matrixRow] [3] ) PrtByte Iя 8; Intensity я DensityTbl[GetAPixel(Col-5, CurrentRow)] + Brightness; if (Intensity > BayerMat [matrixRow] [2] ) PrtByte Iя 4; Intensity = DensityTbl[GetAPixel(Col-6, CurrentRow)] + Brightness; if (Intensity > BayerMat [matrixRow] [1] ) PrtByte Iя 2; Intensity я DensityTbl[GetAPixel(Col-7, CurrentRow)] + Brightness; if (Intensity > BayerMat [matrixRow] [0] ) PrtByte Iя 1; /♦ Теперь мы должны перевести установленные иглы печатающей головки принтера в неустановленное положение, и наоборот. Для этого следует инвертировать значение PrtByte. */ if (PrintMode яя POSPRINT) PrtByte *я OxFF;
Программа печати экрана 279 PrtChar(LPTl, PrtByte); /* посылаем два одинаковых байта */ PrtChar(LPTl, PrtByte); /♦ на принтер. ♦/ if (MaxScreenCol яя 640) /* изображение имеет ширину 640 точек */ PrtChar(LPTl, PrtByte); /♦ посылаем еде два одинаковых ♦/ PrtChar(LPTl, PrtByte); /* байта на принтер. */ PrtString(LPTl, CrLf) ; /♦ возврат каретки и перевод строки */ void PatternPrintCol(unsigned PrintMode, PrinterModes PrtMode, unsigned Col) register unsigned Current Row; register int Density; BYTE RovlData, Rov2Data, Row3Data, Rov4Data; svitch (PrtMode) /* устанавливаем на принтере */ { /* соответствующий графический режим. */ case HighResMode:{ PrtString(LPT1, HighRes); break; case MedResMode:{ PrtString(LPT1, MedRes); break; case LovResMode:{ PrtString(LPTl, LovRes); break; /* co обдаем принтеру количество передаваемых байтов */ PrtChar(LPT1, Я1); PrtChar(LPTl, Я2); f or(CurrentRov=0; CurrentRov<MaxScreenRov; CurrentRov++) { /♦ рассчитываем средник плотность группы из 4 горизонтальных точек */ Density я DensityTbl[GetAPixel(Col, CurrentRow)]; Density +я DensityTbl [Get APixel (Col-1, Cur rent Row)] ; Density +» DensityTbl [Get APixel (Col-2, Current Row)] ; Density +» DensityTbl[GetAPixel(Col-3, CurrentRow)]; Density /я 4; Density +я Brightness; /♦ корректировка яркости */ Density * (Density < 0) ? 0 : Density; Density • (Density > MAXDENSITYINDEX) ? MAXDENSITYINDEX : Density;
280 Глава 7, Создание печатных копий изображений Листинг 7.1. (Продолжение) /♦ определяем структуру печатаемой точки, которая представляет вычисленную плотность */ /♦ смещаем значение на 4 разряда, поскольку эти точки представляют старшие значащие биты для байта управления печатающей головкой */ RovlData я RylanderMat [Density] .Rovl « 4; Row2Data = RylanderMat [Density] .Rov2 « 4; Rov3Data = RylanderMat [Density] . Rov3 « 4; Rov4Data я RylanderMat [Density] .Rov4 « 4; /* рассчитываем среднюю плотность группы из 4 горизонтальных точек */ Density я DensityTbl[GetАРixel(Col-4, CurrentRov)]; Density +я DensityTbl[GetAPixel(Col-5, CurrentRov)]; Density +s DensityTbl[GetAPixel(Col-6, CurrentRov)]; Density +« DensityTbl[GetAPixel(Col-7, CurrentRov)]; Density /» 4; Density += Brightness; /♦ корректировка яркости */ Density я (Density < 0) ? 0 : Density; Density = (Density > MAXDENSITYINDEX) ? MAXDENSITYINDEX : Density; /* определяем структуру печатаемой точки, которая представляет вычисленную плотность ♦/ /* комбинируем значение со старшими значащими битами для байта управления печатающей головкой */ RovlData = RylanderMat [Density] .Rovl « 4; Rov2Data = RylanderMat [Density] .Rov2 « 4; Rov3Data = RylanderMat [Density] .Rov3 « 4; Rov4Data я RylanderMat [Density] .Rov4 « 4; /* если задана печать обратного изображения */ if (PrtMode != POSPRINT) < RovlData *я OxFF; Rov2Data OxFF; Rov3Data *я OxFF; Rov4Data ~= OxFF; PrtChar(LPTl, RovlData); /♦ Посылаем на принтер 4 байта данных. ♦/ PrtChar (LPT1, Rov2Data); /♦ Эти 32 бита представляют 8 бит данных */ PrtChar(LPT1, Rov3Data); /♦ одного элемента изображения. */ PrtChar(LPTl, Rov4Data); PrtString (LPT1, CrLf); /* возврат каретки и перевод строки */ /♦ Данная функция является основной функцией программы печати содержимого экрана. Она печатает на принтере любое изображение, выведенное на экран VGA-монитора. Параметр Rev управляет интерпретацией экранных данных. ♦/ Couplet ionCode Prt Screen (unsigned PrintMode)
Программа печати экрана 281 { int ScreenCol; PrinterModes PrtMode; unsigned CurrentDisplayMode; CurrentDisplayMode = Get VideoMode 0; /♦ определяем текущий видеорежим*/ switch (CurrentDisplayMode) { case LRVIDEOMODE: PrtMode * MedResMode; MaxScreenCol - 320; MaxScreenRow = 200; Is256Colors « TRUE; break; case MRVIDEOMODE: PrtMode * MedResMode; MaxScreenCol = 640; MaxScreenRow “ 200; Is256Colors - FALSE; break; case HRVIDEOMODE: PrtMode * HighResMode; MaxScreenCol * 640; MaxScreenRow = 480; Is256Colors - FALSE; break; default: return (FALSE); /♦ неподдерживаемые видеорежимы ♦/ 1 /♦ Рассчитываем плотности, используемые для вывода изображения. */ InitDensityTbl(); /* Поскольку изображение выводится на экран поколонно, начиная с крайней правой колонки, мы должны вычислить количество байт, посылаемое на принтер, на основании значения MaxScreenRow. Это значение равно либо 200, либо 480. Для печати экрана с 200 строками используется режим принтера с 960 точками. При таком режиме ширина каждого элемента экранного изображения будет на бумаге составлять две точки. Для печати экрана с 480 строками используется режим принтера с 1920 точками. При таком режиме ширина каждого элемента экранного изображения будет на бумаге составлять ширину четыре .точки. */ if ((MaxScreenCol — 320) Aft (Matrix “ BayerMatrix)) { N2 » (MaxScreenRow ♦ 2) » 8; /♦ определяем параметры передачи ♦/ Nl = (MaxScreenRow ♦ 2) ft OxFF; /* данных на принтер */ 1 else
282 Глава 7. Создание печатных копий изображений Листинг 7.1. (Продолжение) { N2 = (MaxScreenRov ♦ 4) » 8; /♦ определяем параметры передачи ♦/ Hl = (MaxScreenRov ♦ 4) & OxFF; /♦ данных на принтер ♦/ } Prtlnit(LPT1); /♦ инициализируем принтер */ PrtString(LPTl, OneDirection); /♦ односторонняя печать ♦/ PrtString(LPT1, DisAutoLf); /* отмена автоматического перевода строки ♦/ PrtString(LPTl, GraphicLineFeed); /♦ перевод строки на 24/216» ♦/ /♦ что необходимо для 8-точечной печати ♦/ /♦ перемещаемся к началу изображения ♦/ for(ScreenCol=MaxScreenCol-l; ScreenCol>=0; ScreenCol -» PIXELSPERPASS) if (kbhitO) /* проверка прерывания программы */ { /* если клавиша нажата ♦/ getchO; /♦ учитываем ее ♦/ break; /♦ и прерываем программу ♦/ if (Matrix == BayerMatrix) DitherPrint Col (PrintMode, PrtMode, ScreenCol); else PatternPrint Col (PrintMode, PrtMode, ScreenCol); PrtString(LPTl, TextLineFeed); /♦ устанавливаем стандартное значение перевода строки для текстового режима ♦/ PrtChar(LPTl, }\f’); /♦ вывод листа бумаги из принтера ♦/ PrtString(LPTl, TvoDirection); /♦ режим двусторонней печати ♦/ return(NoError); /♦ Данная функция выдает сообщение в случае ошибки оператора. После выдачи сообщения выполнение программы прерывается. */ void ShovHelp(void) printf("\nThiв program displays and prints a .PCX file. lHage\n"); printf("Bust be 320x200 in 256 colors, 640x200 in 16 colors or\n"); printf("640x480 in 16 colors.\n\n"); printf("Program is invoked as follovs:\n\n"); printf ("Usage: print [-v -нВ/R -b(+/- 0-9) -n] filename <cr>\n"); printf(" -v displays info about the .PCX file\n"); printf (" -m selects the dither matrix to use:\n"); printf (" В = Bayer ordered dither\n") ; printf (" R = Rylander recursive halftoning\n"); printf(" -b alters brightness\n"); printf(" -n prints negative imageXn"); printf (" filenane is the пане of a .PCX image f ile\n\n") ;
Программа печати экрана 283 exit(l); void main(argc, argv) short argc; char *argv []; { unsigned FileNameCounter» Argindex; char «InFileName; InitGraphics(); printf ("Print Program — Displays and Prints a .PCX Image\n"); printf (" Ver: %d.%d by Craig A. Lindley \n\n\n", ver, rel); delay(1000); /* устанавливаем значения по умолчании */ Verbose я FALSE; /♦ отмена вывода информации о .PCX-файле ♦/ Brightness я 0; /* используем рассчитанную яркость */ PrintMode я POSPRINT; /♦ позитивная печать ♦/ Matrix = BayerMatrix; /* используем матрицу Байера */ Filenamecounter я 0; /♦ счетчик имен файлов */ for(ArgIndex*1; Arglndex<argc; Arglndex++) { if (♦ argv[Argindex] !«’->) /♦ если не ключ командной строки, ♦/ { /♦ значит, имя файла */ if (FileNameCounter >1) /♦ разрешено только одно имя */ ShowHelpO; /♦ если больше - выход по ошибке */ InFileName = argv [Argindex]; /♦ записываем имя файла */ FileNameCounter++; /♦ продолжаем поиск ошибки ♦/ else /♦ ключ командной строки ♦/ { switch (♦ (argv [Argindex] +1)) /* обработка командной строки */ { case ’v’: case ’V’: Verbose я TRUE; break; case ’m’: case ’M’: if ((*argv[Argindex]+2) яя ’r’) | | (*argv[Argindex]+2) яя ’R’)) Matrix я RylanderMatrix; /« используем матрицу Риландера «/ else Matrix я BayerMatrix; /♦ используем матрицу Байера */ break; case ’b’: case ’B’: sscanf (argv [Argindex] +2, "7,d", ftBrightness) ; if (abs(Brightness) > 9)
284 Глава 7. Создание печатных копий изображений Листинг 7.1. (Продолжение) { printf ("Error - invalid brightness specif ied\n") ; ShowHelpO; break; case ’n’: case ’N’: PrintMode = NEGPRINT; break; default: printf ("Error - invalid end line switch encountered\n"); ShowHelpO; > if (FileNaneCounter != 1) { printf("Error - a PCX filenane is required\n"); ShowHelpO; /* Вывод на экран заданного изображения в формате PCX */ DisplayPCXFile(InFileNnae, Verbose); /* Печать изображения с экрана */ if (PrtScreen(PrintMode) !« NoError) restorecrtnodeO; closegraph(); printf("Unsupported video node being utilized\n\n"); exit(FALSE); rest oreertnode(); closegraph(); Работа программы печати экрана Код этой программы можно четко разделить на четыре раздела: код интерфейса принтера, код расчета интенсивности, код печати экрана и, наконец, код интер- фейса пользователя. Каждый из этих разделов мы обсудим отдельно. Во время обсуждения обращайтесь к листингу 7.1. Код интерфейса принтера представляет собой программный интерфейс ме- жду кодом печати экрана в демонстрационной программе и системой BIOS. Этот интерфейс образован функциями «Prtlnit», «PrtStatus», «PrtChar» и «PrtString». Каждая из этих функций прямо или косвенно использует в работе поддержку принтера системой BIOS. Принтерные функции BIOS доступны через программ- ное прерывание 17 hex. Для доступа к нужной функции BIOS программа помеща- ет код команды в регистр «ah» для указания на требуемую функцию. В регистр
Программа печати экрана 285 «dx» помещается номер принтера (допустимыми значениями номера являются значения от 0 до 3). Затем программа заполняет другие необходимые ей реги- стры и выполняет прерывание 17 hex, вызывая выполнение нужной функции. Ниже приведен короткий фрагмент кода, иллюстрирующий описанную схему действий. BYTE PrtChar(unsigned PrtNum, BYTE Character) while(! (PrtStatus(PrtNum) & PRTBUSYBIT) ) ); /* ожидание готовности ♦/ regs.h.ah = PRTCHARCODE; /♦ команда печати символа */ regs.h.al = Character; /♦ печатаемый символ ♦/ regs.h.dx = PrtNum; /♦ выбранный принтер ♦/ int86(PRINTERINT, Aregs, Aregs); /♦ печать символа ♦/ return(regs.h.ah); /♦ возвращает статус операции ♦/ 1 В представленном фрагменте команда печати символа помещается в регистр «ah», печатаемый символ — в регистр «а1», а номер принтера — в регистр «dx». Затем выполняется прерывание 17 hex с помощью функции «int86». После воз- вращения управления от системы BIOS, статус принтера записывается в регистр «ah». Этот статус возвращается в вызывающую программу оператором «return». Действия, выполняемые функциями интерфейса принтера, полностью описы- ваются их названиями. Функция «Prtlnit» инициализирует выбранный принтер, т. е. переводит присоединенный принтер в известное состояние, необходимое для проведения процесса печати. Функция «PrtStatus» возвращает текущий статус принтера. Этот статус подробно рассматривался в гл. 1 и кратко проиллюстри- рован ниже: Функция «PrtChar» посылает символ на принтер для его печати. Эта функ- ция была показана и обсуждена выше. Функция «PrtString» направляет на прин- тер строку символов ASCIIZ. Это позволяет напечатать целую строку с помощью одного вызова функции. При этом замыкающий нулевой символ на принтер не передается. Эти четыре функции представляют собой все, что нужно для взаи- модействия кода печати графического экрана с физическим принтером. Следующей стадией в организации кода графической печати является рас- чет и масштабирование значений интенсивностей элементов изображения. Как
286 Глава 7. Создание печатных копий изображений уже говорилось, программа печати может воспроизводить 17 уровней интенсив- ности на печатной копии изображения. По этой причине все возможные значе- ния интенсивностей в исходном изображении должны быть промасштабирова- ны в интервале от 0 до 16. Это необходимо для правильной работы алгоритмов матрицирования и псевдотонирования. При обсуждении графического адаптера VGA в гл. 1 уже говорилось о том, что значение точки на экране не является цветом этой точки, а скорее индексом (прямо или косвенно через палитру) цвето- вого регистра, который в действительности содержит компоненты цвета данной точки. Для того чтобы определить интенсивность элемента изображения для его масштабирования, сначала нужно определить компоненты соответствующе- го регистра цвета в формате RGB. Если регистр цвета представляет собой уро- вень серого цвета (когда все три составляющие цвета равны друг другу), уровень интенсивности равен значению любой компоненты цвета. Если же цвет элемен- та отличается от серого, интенсивность должна быть рассчитана по следующей формуле, найденной в техническом руководстве фирмы IBM: Интенсивность = 30% * Красный + 59% * Зеленый + 11% * Синий Максимальная интенсивность любого элемента, будь он серый или цветной, равна 63. Все рассчитанные интенсивности элементов изображения должны на- ходиться в интервале от 0 до 16. Это достигается путем умножения значения интенсивности на 16 и деления произведения на 63. В действительности этот процесс выглядит несколько сложнее, поскольку в нем используется целочи- сленная арифметика. Его детали можно увидеть в листинге 7.1. Таблица с именем «DensityTbl» заполняется всеми промасштабированными значениями интенсивности, допустимыми для данного количества цветов. Для изображения с 256 цветов таблица «DensityTbl» будет содержать 256 элемен- тов, а для изображения с 16 цветов — 16 элементов. Каждый элемент таблицы «DensityTbl» находится в диапазоне от 0 до 16. Для получения промасштабиро- ванного значения интенсивности для любого элемента исходного изображения нужно использовать интенсивность этого элемента в качестве индекса в таблице «DensityTbl». Величина, найденная по этому индексу, будет представлять собой промасштабированное значение интенсивности для данного элемента. Отсюда становится ясно, что многие из цветов, доступных в режиме с 256 цветов, бу- дут промасштабированы с одними и теми же значениями интенсивности. Ведь все 256 цветов нужно напечатать, используя те же 17 уровней интенсивности, что и для режима с 16 цветами. Как легко догадаться, качество 256-цветовых изображений при этом будет ухудшаться. После того как таблица «DensityTbl» будет построена, изображение мож- но печатать. На этой стадии работы программы предполагается, что исходное изображение уже считано с диска и выведено на экран. Изображение будет пе- чататься непосредственно с экрана монитора. О том, как считать и вывести на экран файл формата PCX, написано в гл. 6, а о том, как считывать элементы с экрана VGA — в гл. 1. Изображение печатается путем сканирования справа налево, причем каждый раз печатается представление одной колонки элементов исходного изображения. Таким образом, выходное изображение будет повернуто на 90 градусов против часовой стрелки. Крайняя правая колонка изображения станет верхней строкой
Программа печати экрана 287 печатной копии, а крайняя левая колонка — нижней строкой. Процесс печати запускается путем вызова функции «PrtScreen». Функция «PrtScreen» разработана как самостоятельная функция, которая может вызываться любой из программ обработки изображений в этой книге для создания печатной копии VGA-дисплея. По этой причине функция «PrtScreen» самостоятельно получает информацию о состоянии дисплея VGA (текущее раз- решение, режим, число цветов и т. д.), вместо того, чтобы использовать информа- цию от функций поддержки формата PCX, которые используются для загрузки изображения с диска и вывода его на экран. Функция «GetVideoMode» из библиотеки функций адаптера VGA, предста- вленной в гл. 1, используется для определения текущего статуса видеодисплея. Как уже говорилось, функция «PrtScreen» поддерживает только три видеоре- жима. Если текущий режим не поддерживается, возвращается значение ошибки и выполнение программы прекращается. Зная текущий видеорежим, легко определить, какое число строк и столбцов составляют изображение и сколько цветов используются для его отображения. На основании информации о количестве цветов функция «InitDensityTbl» со- здает таблицу «DensityTbl», о которой уже говорилось. Затем, рассчитывается общее количество байт, которые составляют одну печатаемую колонку (линию). Это количество зависит от разрешения печатаемого изображения. Затем на прин- тер посылается строка управляющих символов, чтобы перевести его в нужный режим. Учтите, что эти последовательности управляющих символов применимы только для IBM-совместимых принтеров. Если вы используете другой принтер, замените эти последовательности соответствующим образом. При работе с IBM- совместимым принтером нужно перевести его в режим односторонней печати, чтобы обеспечить правильную печать изображения. Двусторонняя печать при- водит к тому, что колонки графического изображения становятся волнообраз- ными. Кроме того, следует изменить межстрочное расстояние по сравнению с текстовым режимом. В противном случае вы получите белые промежутки ме- жду строками графического изображения. После завершения инициализации принтера программа начинает цикл, ко- торый печатает восемь элементов исходного изображения за раз (перемещаясь справа налево). Если во время печати пользователь нажимает клавишу на кла- виатуре, то цикл и соответственно программа печати прерывают свою работу. Способ печати восьми точек исходного изображения зависит от выбранного ал- горитма печати. Если используется матрицирование, то вызывается функция «PattemPrintCol», а если применяется метод псевдотонирования — функция «DitherPrintCol». После того как все колонки исходного изображения будут вы- ведены на печать, принтер опять переводится в текстовый режим с помощью по- следовательности управляющих символов. На принтер также посылается коман- да «form feed» для того, чтобы освободить напечатанный лист бумаги. Если во время процесса печати не происходит каких-либо ошибок, функция «PrtScreen» возвращает код «NoError». Функция «PattemPrintCol» использует метод матрицирования для генерации печатаемого изображения. Как уже говорилось, матрицирование приводит к по- тери разрешения изображения, если только разрешающая способность устрой- ства вывода не превышает разрешение исходного изображения. В нашем случае
288 Глава 7. Создание печатных копий изображений разрешение принтера в горизонтальном направлении в четыре раза превыша- ет разрешение изображения, а в вертикальном направлении они практически совпадают. По этой причине горизонтальное разрешение изображения (которое соответствует вертикальному разрешению принтера) будет потеряно при печати методом матрицирования. Девятиигольчатая печатающая головка принтера печатает восемь графиче- ских точек за один проход. Ниже приведено соответствие между байтом данных, посланным на принтер, и активируемыми иглами печатающей головки. Номер иглы Ориентация точек Номер бита Десятичное значение 8 Верхняя игла 7 (128) 7 6 (64) 6 5 (32) 5 4 (16) 4 3 (8) 3 2 (4) 2 1 (2) 1 Нижняя игла 0 (1) Из приведенной таблицы видно, как генерируются отдельные графические точки. Для включения самой верхней иглы (номер 8) печатающей головки на принтер следует послать значение 128. Для того чтобы напечатать сразу 8 то- чек, надо использовать значение 255, а для того чтобы переместить печатающую головку без печати — значение 0. В любом случае печатающая головка пере- мещается в следующее физическое положение после печати каждой колонки точек. Информация о том, какие из точек должны быть напечатаны, рассчитыва- ется на основании данных исходного изображения. Вычисление проводится над двумя группами из четырех элементов изображения. Этот процесс можно пред- ставить в следующем виде. 1. Значения правой группы из четырех элементов изображения считываются с экрана VGA. 2. Масштабированные значения интенсивности для каждого из четырех эле- ментов определяются из таблицы «DensityTbl». 3. Определяется усредненная интенсивность для данной группы элементов. Отметим, что именно здесь происходит потеря разрешения. Группа из че- тырех элементов исходного изображения используется для получения од- ного матрицированного элемента. 4. Множитель для уточнения яркости добавляется к средней интенсивности в том случае, когда пользователь задает режим коррекции яркости. 5. Уточненное значение интенсивности используется для получения соответ- ствующих данных матрицирования из матрицы Риландера. Эти данные подвергаются сдвигу и помещаются в строку переменных 1-4, которая за- тем посылается на принтер. Эти данные управляют верхними 4 иглами печатающей головки принтера.
Программа печати экрана 289 6. Шаги 1-5 повторяются еще раз для второй группы из четырех горизон- тальных элементов. В этот раз, однако, матрицированные данные сме- шиваются с предварительно сдвинутыми и загруженными матрицирован- ными данными. Эти данные управляют нижними 4 иглами печатающей головки принтера. 7. Если требуется печать негативного изображения, каждый байт данных инвертируется. Это вызывает преобразование черного цвета в белый и на- оборот. 8. Наконец, четыре байта принтерных данных, соответствующих восьми го- ризонтальным элементам изображения, посылаются на принтер. 9. Этот процесс повторяется для каждой строки исходного изображения. Это означает, что при каждом вызове функции «PattemPrintCol» печатаются восемь элементов каждой строки исходного изображения. После заверше- ния этого процесса на принтер подается команда «carriage return», которая вызывает печатание полной строки графических кодов за один проход. Код, приведенный в листинге, поможет вам яснее представить себе этот про- цесс. Этот код труднее описать, чем применить. Функция «DitherPrintCol» по своему действию значительно более проста. Она является непосредственной реализацией алгоритма псевдотонирования, описан- ного в тексте. Действие этой функции можно описать следующим образом. 1. Промасштабированное значение крайнего правого элемента исходного изо- бражения считывается с дисплея и преобразуется с помощью таблицы «DensityTbl». 2. Это значение уточняется с помощью определяемого пользователем фак- тора яркости, как описано выше. 3. Если эта уточненная интенсивность превышает значение соответствующе- го элемента матрицы псевдотонирования, в переменную «PrtByte» загру- жается десятичное значение, активирующее самую верхнюю иглу печата- ющей головки. После слияния других значений элементов изображения в переменную «PrtByte» она посылается на принтер. Соответствие между элементом изображения и элементом матрицы псевдотонирования поддер- живается с помощью переменной «MatrixRow» и индекса колонки матри- цы. Новое значение параметра «MatrixRow» рассчитывается для каждой строки исходного изображения. Индекс колонки матрицы для матрицы Байера всегда остается постоянным. 4. Шаги 1-3 повторяются для всех остальных семи элементов исходного изо- бражения, которые обрабатываются за один вызов функции «DitherPrint- Col». 5. Если выводится позитивное изображение (т. е. изображение, у которого более светлые области печатаются с большим содержанием белого цвета), то байт «PrtByte» должен быть инвертирован. Это необходимо потому, что смысл печатающих игл является противоположным. 6. Наконец, байт «PrtByte» посылается на принтер. Этот процесс повторя- ется два раза для йзображений 320 х 200 и четыре раза — для изображе- 19-3
290 Глава 7. Создание печатных копий изображений ний шириной 640 точек. Это помогает скорректировать соотношение геоме- трических размеров изображения. Байт «PrtByte» можно направлять на принтер несколько раз, поскольку разрешение принтера в горизонтальном направлении выше, чем в вертикальном. 7. Этот процесс повторяется для каждой строки исходного изображения. Это означает, что при каждом вызове функции «DitherPrintCol» печатаются восемь элементов каждой строки исходного изображения. После заверше- ния этого процесса на принтер подается команда «carriage return», которая вызывает печатание полной строки графических кодов за один проход. Последней частью демонстрационной программы печати, которую нам сле- дует обсудить, является код интерфейса пользователя. Этот код содержится в функциях «main()» и «ShowHelpO». Общий подход к обработке параметров, введенных пользователем, подробно описан в гл. 5. Так, программа получения цветных изображений (листинг 5.4) использует тот же тип пользовательского интерфейса. Наш код отличается не тем, как он работает, а теми опциями, ко- торые он реализует. Данная демонстрационная программа распознает четыре различных пере- ключателя командной строки. Во-первых, если определен переключатель «-V», пользователю будет предоставлена распечатка информации о файле PCX. Эта опция полностью совпадает с такой же опцией программы «view» из гл. 6. Если определен переключатель «-ш», за ним должен следовать либо символ «Ь/В», либо «г/R» для указания на определенный алгоритм печати. По умолчанию ис- пользуется псевдотонирование по Байеру, если только пользователь не исполь- зует ключ «-mR», который определяет матрицирование по Риландеру. Яркость печатаемого изображения может быть уточнена путем установки ключа «-Ь», за которым должно следовать значение фактора коррекции в диапазоне от «—9» до «Н-9». Чем больше значение фактора, тем более светлым будет печатаемое изображение. Последний ключ командной строки «-п» вызывает печать нега- тивного изображения. Демонстрационная программа печати будет напоминать пользователю правильный формат переключателей командной строки в случае использования неправильных переключателей или вызова программы с ключом «?» в качестве параметра, как показано ниже: print ? <Enter> На рис. 7.5 показаны два различных изображения, напечатанные демонстра- ционной программой с использованием методов матрицирования и псевдотониро- вания. Использование псевдотонирования приводит к более высокому качеству изображения. Матрицирование с учетом неизбежной потери разрешения почти полностью разрушает текст в печатаемых изображениях. В общем случае вы мо- жете видеть в матрицированных изображениях более грубую текстуру и потерю некоторых деталей. Псевдотонированные же изображения, напротив, сохраняют практически все детали исходных изображений. Выбор метода получения твер- дой копии существенно зависит от содержания исходного изображения и того, какой эффект вы хотите получить на бумаге. Для решения этого вопроса луч- шим способом является экспериментирование.
Заключение 291 Заключение На этом мы заканчиваем обсуждение методов получения печатных копий изо- бражения. Мы узнали о том, как использовать фотографические методы для получения твердой копии компьютерного изображения, как технология полу- тонирования применяется для создания иллюстраций в полиграфии, как такое полутонирование может быть смоделировано на принтере с помощью матрици- рования или псевдотонирования и, наконец, как эти алгоритмы могут быть ис- пользованы в программе. Эта глава также завершает ч. I книги. Гл. 1 была посвящена основам, необ- ходимым для полного понимания всей остальной информации, представленной в книге. Гл. 2 научила нас основам видеотехники и подготовила к созданию ви- деопреобразователя, описанного в гл. 3. Главы 4 и 5 рассказали о программном обеспечении, необходимом для работы видеопреобразователя. В гл. 6 обсужда- лись графические форматы и программный код, который позволяет сохранить полученное изображение в удобном и популярном формате графического файла. Наконец, гл. 7 показала нам, как выведенные на экран изображения могут быть преобразованы для получения печатной копии на графическом принтере. Теперь, когда все это осталось позади, мы можем перейти к ч. II книги, в ко- торой рассмотрены различные алгоритмы обработки изображений. Многие ме- тоды, описанные в ч. I, будут использованы и в ч. II. Часть II содержит также много новых методов и идей. Пожалуйста, сядьте поудобнее и расслабьтесь. Пу- тешествие в царство обработки изображений начинается.
Часть II Классические методы обработки изображения Введение Здесь мы оставим обсуждение проекта видеопреобразователя и сконцентрируем- ся на обработке изображения. В определенном смысле обе части книги не зависят друг от друга. В ч. I показано, как можно получить изображения и хранить их, а в ч. II показано, как могут быть обработаны изображения. Обработанные изо- бражения в этой части книги могут быть получены из различных источников. Для этого совсем не обязательно использовать видеопреобразователь, описанный в ч. I. Рассматриваемые функции обработки изображения будут выполняться с любыми изображениями в формате PCX. Каждая глава в ч. II строится следующим образом: 1. Наряду с текстовой информацией относительно класса алгоритмов обра- ботки изображения рассматриваются проблемы, затрагивающие исполь- зование этих алгоритмов. 2. Рассматриваются тестовые изображения, которые демонстрируют эффект применения алгоритмов. 3. Краткое изложение Си-функций, реализующих рассматриваемые алгорит- мы. Совокупность этих функций в каждой главе называется библиотекой функций. Каждая Си-функция описывается подробно в библиотеке функ- ций. 4. Листинг кодов, которые образуют библиотеку функций. Обычно это пер- вый листинг в главе. 5. Пример программы, которая не только демонстрирует, как функциониру- ет библиотека функций, но и то, как генерируется каждое из тестовых изображений, описанных в главе. Все исходные тесты образуют так называемую библиотеку функций обработ- ки изображения. Каждая глава в ч. II книги прибавляет новые функции в эту библиотеку.
Введение 293 С помощью этой библиотеки и персонального компьютера вы можете прово- дить эксперименты с методиками, которые ранее использовались только в фо- толабораториях или в лабораториях по обработке изображений ведущих корпо- раций. Эти методы более удобны, чем фотолаборатория, так как вам не придет- ся беспокоиться о химикатах, тратить материалы и ждать, чтобы изображение просохло. Приветствуется экспериментирование с описанными методами и текстами программ. При условии качественного интерфейса пользователя приведенные тексты могут быть превращены в весьма мощную программу обработки изобра- жения. Выполнение алгоритмов обработки изображения позволит широко экс- периментировать с различными изображениями и комбинациями этих функций. Когда вы получаете изображение, которое хотите сохранить, вы можете исполь- зовать функции поддержки PCX или TIFF, приведенные в гл. 6. Кроме того, функции печати, приведенные в гл. 7, смогут выводить на принтер черно-белые версии ваших изображений. Вся обработка изображения осуществляется при разрешеции 320 х 200. Та- кое разрешение обусловлено числом имеющихся цветов (256). Используются ис- ключительно полутоновые изображения. Каждый элемент изображения может иметь 64 уровня серого цвета. Таким образом, значение элементов изображения составляет по крайней мере 6 бит. Все изображения отсканированы с помощью видеопреобразователя, описанного в ч. I, и программы «acquire.c», представлен- ной в гл. 12. Цветные изображения могут быть обработаны некоторыми алгоритмами об- работки изображения, но результаты обычно оставляют желать лучшего. При- чины таких результатов станут понятными по мере того, как будет рассмотрен и усвоен материал следующих пяти глав. Изображения с высоким разрешени- ем также могут быть обработаны с небольшими изменениями кода обработки изображения. Требуемые изменения описаны в гл. 8. Большинство алгоритмов, представленных в части II, требуют достаточно много времени. Причинами этого являются следующие: а) Все алгоритмы написаны на языке Си. При этом не делалось попыток использовать язык ассемблера для увеличения производительности. б) Никакие ухищрения на языке Си не использовались. Исходные тексты очень понятны и написаны они скорее в стиле языка Pascal. При этом ясность кода имела преимущество над скоростью. в) Используются числа с плавающей запятой. Некоторые из алгоритмов тре- буют использования реальных чисел или чисел с плавающей запятой. Компиляторы Си, оснащенные библиотеками с плавающей запятой, очень медленны. Если производительность становится для вас важным фактором, купите ма- тематический сопроцессор и затем попробуйте переписать некоторые функции на ассемблере. По-видимому, можно достичь повышения производительности на порядок при относительно небольших изменениях в исходных текстах программ.
294 Обработка изображения Обработка изображения — это область науки, связанная с изображениями и данными изображений. Она охватывает большой спектр методов, которые име- ют очень широкое применение. Обработка изображения является специальной формой двумерной обработки сигнала, используемой для извлечения информа- ции об изображении. В общем случае методы обработки изображения применимы к данным изображений, когда: а) Необходимо повышение качества изображения или модификация для то- го, чтобы выделить некоторые аспекты информации, содержащиеся в изо- бражении. б) Необходимо классифицировать, сравнить или измерить элементы в изо- бражении. в) Необходимо скомбинировать части изображений или реорганизовать эле- менты изображения. Методы обработки изображения можно применить к данным, даже если они представлены в беззнаковой форме. Манипуляции с данными видимых изобра- жений — это одно из основных применений обработки изображений, вероятно, даже самое основное. Обработка изображения может быть использована для того, чтобы получить видимое изображение цифровых данных, усиленных опре- деленным образом для выделения некоторых аспектов данных. Такие типы об- работки изображения используются, например, в медицинском оборудовании, гидролокаторах, радарах, ультразвуковом оборудовании, теплочувствительном оборудовании и т. д. Применение алгоритма обработки к изображению не всегда делает хорошим его внешний вид. Но не только эстетика является критерием, по которому мож- но судить об эффективности трансформации. Если трансформация приносит до- полнительную информацию и/или детали, невидимые в исходном изображении, результат может считаться успешным, даже если он выглядит не очень красиво. В ч. II рассматриваются 4 различных класса алгоритмов обработки изобра- жения: а) Точечные процессы. Эти процессы изменяют значения элементов в изо- бражении, основываясь на исходных значениях элементов и возможную их позицию в изображении. б) Пространственные процессы. Эти процессы изменяют значения элементов изображения, основываясь на исходных значениях элементов и элементов вокруг него. в) Покадровые процессы. Эти процессы изменяют значение элементов в изо- бражении, основываясь на значениях элементов, присутствующих в одном или более дополнительных изображениях. г) Геометрические процессы. Эти процессы изменяют расположение или ме- стонахождение элементов в изображении, основываясь на некоторых гео- метрических преобразованиях. Множество различных функций обработки изображения предусмотрено в каг ждом из перечисленных выше классов. Эти функции могут применяться как не-
Обработка, изображения 295 посредствен© к изображению, так и в совокупности с другими функциями либо в том же классе, либо в различных классах. Пожалуйста, запомните, что примене- ние этих алгоритмов не коммутативно, т. е. очень важен порядок их применения. В этой книге приведены, конечно, не все возможные алгоритмы обработки изображения каждого из классов. Базовые алгоритмы описаны достаточно по- дробно, тогда как редкие алгоритмы не рассмотрены вовсе. Во многих книгах по обработке изображения, перечисленных в ч. III, описаны детали наиболее непонятных алгоритмов. Приведенные здесь алгоритмы обработки изображения описываются подроб- но и рассматриваются в плане выполнения и получения полезного результата. Результатом этого рассмотрения является то, что алгоритмы обработки изобра- жения могут быть использованы по принципу «книги для домохозяек» без пол- ного понимания их математической поддержки. Конечно, результаты были бы лучше, если бы математика была полностью понятна. Для этого даны некоторые математические выкладки, и в другой литературе вы сможете более детально с ними ознакомиться. Прежде чем начать обсуждение, необходимо определить несколько терминов, которые будут использованы на протяжении всей второй части книги. Определение их применительно к обработке изображения, дано в «Толковом словаре терминов» в ч. III. Пространственная разрешающая способность — это число выборок, использу- емых для определения изображения. Чем выше пространственная разрешающая способность, тем выше качество изображения. Под пространственной разреша- ющей способностью обычно понимают двумерную величину, организованную в колонки и ряды. Пересечения каждой колонки и ряда содержит вычисленный образец, взятый из изображения. Изображения с пространственной разрешаю- щей способностью 320 х 200,640 х 200 и 640 х 480 могут быть получены с помощью видеопреобразователя, описанного в ч I. Пространственная частота — это ско- рость изменения интенсивности элементов изображения. Ее представляют дву- мерной величиной, так как интенсивность элементов изображения меняется од- новременно как в горизонтальном, так и в вертикальном направлениях. Части изображения, которые имеют постоянные значения интенсивности элементов, имеют низкое содержание пространственной частоты. Части со значениями ин- тенсивных элементов, подверженных сильной флуктуации, имеют высокое со- держание пространственной частоты. Яркостное разрешение — это общее число значений, используемых для того, чтобы выразить значение интенсивности элементов в изображении. Яркостное разрешение связано с числом бит, используемых для хранения каждого элемента изображения. Например, для 6 бит каждого элемента изображения возможно общее число уровней яркости 64.
Глава 8 Функции поддержки обработки изображения В главе рассмотрены следующие вопросы: • Общая организация программного обеспечения обработки изображения. • Функции поддержки, содержащиеся в библиотеке функций. Введение Прежде чем начать рассмотрение самих алгоритмов обработки изображения, мы должны изучить функции поддержки, на которых эти алгоритмы основаны. Они образуют интерфейс между данными изображения в буфере и алгоритмами обработки изображения. Каждый байт данных изображения, полученных с по- мощью алгоритмов обработки изображения, «проходит» через эти функции под- держки, которые осуществляют только манипуляции с данными изображения: они не связаны с чтением или записью данных изображения в файлы на диске. Другими словами, эти функции не имеют отношения к тому, откуда «пришли» данные изображения или куда они «идут», а только с их манипуляцией. Откуда берутся данные изображения, с которыми манипулируют функции поддержки? Они могут прийти из различных источников. Для нашей обработ- ки изображения в ч. II все данные изображения поступают из РСХ-файлов (см. гл. 6 о функциях PCX). Данные могут поступать непосредственно из видеопре- образователя, сканера, точечной программы или других графических файлов. Для упрощения задачи насколько это возможно и для рассмоторения толь- ко обработки изображения мы будем использовать исключительно РСХ-файлы для всех входных и выходных данных изображения. На рис. 8.1 показана блок- диаграмма, которая иллюстрирует иерархию используемого нами программного обеспечения для полной обработки изображения. Функции поддержки обработки изображения очень просты для расчета и эксплуатации. Они были специально рассчитаны для разрешения изображений 320 х 200 с использованием 64 уровней яркости или 256 цветов, но могут рабо- тать с любым разрешением изображения. Производительность — это ключ при расчете функций поддержки. Эти программы могут быть вызваны 64000 раз во время преобразования изображения, следовательно, очень важно, чтобы они вы- полнялись быстро. В профессиональной программе обработки изображения, возможно, эти про- граммы могут быть переписаны в языке ассемблера для того, чтобы получить максимальную возможную производительность. Для наших же целей произво- дительность программного кода на языке Си будет вполне достаточна. Для использования функций поддержки необходимо включить в код выпол- нения файл imagesup.с с выполнением. Оба файла показаны в листинге 8.1.
Введение 297 VGA-монитор В PCX-файл Из РСХ-файла Рис. 8.1. Блок-диаграмма иерархии программного обеспечения.
298 Глава 8. Функции поддержки обработки изображения Обычно файл заголовка содержит функцию прототипа функций поддержки. По этой причине код выполнения, который включает в себя файл заголовка, внеш- не не заявляет о функциях поддержки до того, как они будут использованы. Функции поддержки обработки изображения не имеют файла создания проек- та, потому что они самостоятельно не применяются. Чтобы пользоваться этими функциями поддержки, необходимо включить их в файл создания проекта. В гл. 9 показан пример использования файла создания проекта «pttest.prj». Отметим, что хотя функции поддержки обработки изображения являются достаточно общими для обработки изображения с любым разрешением, тем не менее некоторые коды алгоритмов обработки изображения не годятся. Необходи- мо изменить два определения в файле imagesup .h «MAXCOLS» и «MAXROWS», если используются изображения с разрешением, отличным от 320 х 200. Листинг 8.1. Функции поддержки обработки изображения Ниже приводится содержимое файла image sup. h: /****************************************/ /* Header файл ♦/ /♦ Функции обработки изображений */ /* разработан в Turbo С 2.0 */ /* Крэйтон А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 11/14/89 */ /****************************************/ /♦ Этот файл содержит общие опеределения, используемые во всех программах обработки изображений, которые приводятся в ч. II книги. Предполагается использование изображения размером 320x200 точек. Если пользователь намеревается работать с другими типами изображений, он должен соответствующим образом изменить параметры MAXCOLS и MAXROWS. ♦/ /* Информция об элементах изображения ♦/ «define MAXSAMPLEBITS 6 /♦ 6 бит от вмдеопреобразователя ♦/ «define MINSAMPLEVAL 0 /♦ минимальное значение элемента « 0 ♦/ /♦ Максимальное число значений элементов изображения ♦/ «define MAXQUANTLEVELS <1<<MAXSAMPLEBITS) /♦ Максимальное значение элемента изображения = 63 */ «define MAXSAMPLEVAL (MAXQUANTLEVELS-1) 0 LRMAXCOLS (MAXCOLS-1) 0 LRMAXROWS (MAXROWS-1) /* Определения разрешения изображений ♦/ «define MINCOLNUM «define MAXCOLS «define MAXCOLNUM «define MINROWNUM «define MAXROWS «define MAXROWNUM /♦ нулевая колонка*/ /♦ всего 320 колонок*/ /♦ номер последней колонки я 319 */ /♦ нулевая строка ♦/ /♦ всего 200 строк ♦/ /♦ номер последней строки = 199 */
Введение 299 «define RASTERSIZE ((long)MAXCOLS*MAXROWS) «define MAXNUMGRAYCOLORS MAXQUANTLEVELS /♦ Определения гистограмм ♦/ «define HISTOCOL «define HISTOROW «define HISTOWIDTH «define HISTOHEIGHT 0 0 134 84 «define BLACK 0 «define WHITE 63 «define AXISCOL (HIST0C0L+3) «define AXISROW (HIST0R0W+HIST0HEIGHT-5) «define AXISLENGTH MAXQUANTLEVELS«2-1 «define DATACOL AXISCOL «define DATAROW AXISROW-1 «define MAXDEFLECTION (HISTOHEIGHT-1O) /♦ Прототипы внешних функций ♦/ void Copy Image (BYTE huge «SourceBuf, BYTE huge « DestBuf) ; BYTE GetPixelFromImage(BYTE huge «Image,unsigned Col, unsigned Row); CompletionCode PutPixellnlmage(BYTE huge «Image, unsigned Col, unsigned Row, unsigned Color); CompletionCode DrawHLine(BYTE huge «Image, unsigned Col, unsigned Row, unsigned Length, unsigned Color); CompletionCode DrawVLine(BYTE huge «Image, unsigned Col, unsigned Row, unsigned Length, unsigned Color); void ReadlmageAreaToBuf(BYTE huge «Image, unsigned Col, unsigned Row, unsigned Width, unsigned Height, BYTE huge «Buffer); void Writ e Image Ar eaFromBuf (BYTE huge «Buff er,unsigned Buf Width, unsigned Height, BYTE huge «Image, unsigned ImageCol, unsigned ImageRow); void ClearImageArea((BYTE huge «Image, unsigned Col, unsigned Row, unsigned Width, unsigned Height, unsigned PixelValue); CompletionCode Paramet erCheckOK (unsigned Col, unsigned Row, unsigned ColExtent, unsigned RowExtent, char «ErrorStr); Далее следует содержимое файла imagesup.с:
300 Глава 8. Функции поддержки обработки изображения Листинг 8.1. (Продолжение) /де**************************************/ /* Функции пространственной обработки */ /♦ разработаны в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли */ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 11/14/89 */ /««««*«деде«**«деде«деде«деде*****«деде««/ «include <stdio.h> «include <process.h> «include <conio.h> «include <dos.h> «include <alloc.h> «include <nen.h> «include <graphics.h> «include ’’nisc.h” «include ’’pcx.h” «include ’’vga.h” «include ”inagesup.h” extern struct PCX.File PCXData; extern unsigned InageWidth; extern unsigned InageHeight; /♦ Функции поддержки обработки изображений. Пояснения в тексте книги. ♦ / /♦ Копирование полного изображения из буфера источника в буфер приемника. ♦ / void Copy Inage (BYTE huge «SourceBuf, BYTE huge * DestBuf) { novedata(FP_SEG(SourceBuf) ,FP_OFF(SourceBuf) , FP.SEG(DestBuf),FP_OFF(DestBuf), (unsigned) RASTERSIZE); /♦ Примечание. Чтобы использовать индексирование внутри изображения, подобного массиву, индекс должен иметь тип long. При этом нельзя использовать приведение (casting) к типу long. ♦ / BYTE GetPixelFronInage(BYTE huge * Inage, unsigned Col, unsigned Row) { unsigned long PixelBufOffset; if((Col < InageWidth) && (Row < InageHeight)) { PixelBuf Off set = Row; /♦ для предотвращения переполнения ♦/ PixelBufOffset *= InageWidth;
Введение 301 PixelBufOffset +s Col; return( Inage [PixelBuf Offset]); printf("GetPixelFronlnage Error: Coordinates out of range\n"); printf(" Col « Xd Row « %d\n",Col,Row); return(FALSE) ; 1 CompletionCode PutPixellnlnage(BYTE huge *Inage, unsigned Col, unsigned Row, unsigned Color) { unsigned long PixelBufOffset; if((Col < ImageWidth) ftft (Row < ImageHeight)) PixelBuf Off set ж Row; /♦ для предотвращения переполнения */ PixelBufOffset *a ImageWidth; PixelBufOffset +a Col; Inage[PixelBufOffset] a Color; return (TRUE); else printf ("PutPixellnlnage Error: Coordinates out of range\n"); printf(“ Col a %d Row a Xd\n“,Col,Row); return (FALSE); 1 /♦ Примечание. Значение длины 0 подразумевает 1 элемент изображения, значение 1 - два элемента, и т.д. Вот почему значение длины инкрементируется перед использованием. ♦/ ConpletionCode DrawHLine(BYTE huge «Inage, unsigned Col, unsigned Row, unsigned Length, unsigned Color) if ((Col < InageWidth) && ((Col+Length) <= InageWidth) && (Row < ImageHeight)) Length++; while(Length—) PutPixellnlnage(Inage,Col++ ,Row,Color) ; return (TRUE); else printf("DrawHLine Error: Coordinates out of range\n"); printf (" Col = */.d Row a 7.d Length =%d\n",Col,Row,Length) ; return(FALSE);
302 Глава 8. Функции поддержки обработки изображения Листинг 8.1. (Продолжение) ConpletionCode DrawVLine(BYTE huge ♦Inage, unsigned Col, unsigned Row, unsigned Length, unsigned Color) { if ((Row < InageHeight) && ( (Row+Length) <= InageHeight) && (Col < InageVidth)) { Length++; while(Length—) Put Pixel InImage (Image, Col, Row++, Co lor); return (TRUE); else { printf("DrawVLine Error: Coordinates out of range\n“); printf(" Col « %d Row s %d Length »%d\n”,Col,Row,Length); return(FALSE); void ReadlnageAreaToBuf(BYTE huge *Inage, unsigned Col, unsigned Row, unsigned Width, unsigned Height, BYTE huge ♦Buffer) { unsigned long PixelBuf Off set = OL; register unsigned InageCol, InageRow; for(ImageRow=Row; InageRow < Row+Height; InageRow++) for(ImageCol=Col; InageCol < Col+Width; InageCol++) Buffer[PixelBufOffset++] » GetPixelFronlnage (Inage, InageCol, InageRow) ; } void Writ e Inage Ar eaFronBuf (BYTE huge ♦Buff er, unsigned Buf Width, unsigned Height, BYTE huge ♦ Inage, unsigned InageCol,unsigned InageRow) { unsigned long PixelBuf Off set s OL; register unsigned BufCol, BufRow, CurrentInageCol; for(BufRow«0; BufRow < BufHeight; BufRow++) { CurrentInageCol s InageCol; for(BufCol»0; BufCol < BufWidth; BufCol++) { PixelBuf Off set « (unsigned long) Buf RoweBufWidth+Buf Col; Put Pixel Ini mage (Image, Current InageCol, InageRow, Buf f er [PixelBuf Off set] ) ; CurrentInageCol++; InageRow++;
Введение 303 void Clear Image Area ((BYTE huge *Image, unsigned Col, unsigned Row, unsigned Width, unsigned Height, unsigned Pixel Value) { register unsigned BufCol, ButRow; for(BufRowe0; BufRow < Height; BufRow++) for(BuiCol«0; BufCol < Width; BufCol++) PutPixelInImage(Image,BufCol+Col,BufRow+Row,PixelValue); } /♦ Данная функция проверяет правильность параметров, передаваемых функциям обработки изображений. Эти параметры должны находиться в определенном диапазоне. Если введены правильные параметры, функция возвращает значение TRUE. В противном случае выдается сообщение об ошибке и выполнение вызываемой программы прерывается. ♦/ CompletionCode Par amet er Che ckOK (unsigned Col, unsigned Row, unsigned ColExtent, unsigned RowExtent, char ♦FunctionName) { if((Col > MAXCOLNUM) II (Row > MAXROWNUM) II (ColExtent > MAXCOLS) I I (RowExtent > MAXROWS)) restorecrtmodeO; printf (''Parameter(s) out of range in function: %s\n" ,FunctionName); printf (" Col a 7.d Row=7.d ColExtent = 7.d RowExtent » 7.d\n", Col, Row, ColExtent, RowExtent); exit(EBadParms); return(TRUE); } Функции поддержки обработки изображения создаются с помощью девяти функций. Они приведены в табл. 8.1, а их определение показано в листинге 8.1. Функции «GetPixelFromlmage» и «PutPixellnlmage» заслуживают особого вни- мания. Функции «GetPixelFromlmage» и «PutPixellnlmage» являются основны- ми для всех функций поддержки и всех алгоритмов обработки изображения, которые будут описаны ниже. Они обрабатывают данные изображения как мас- сив байт. Массив типа huge используется потому, что буфер изображения может быть длиной более 64 Кбайт. Как говорилось в гл. 1, для работы с массивом типа huge должен быть использован индекс, представляющий собой длинное це- лое число (long integer). По этой причине вводится индекс типа unsigned long, названный «PixelBufOffset» для доступа к массиву данных изображения. Приве- дение индекса к типу long перед его использованием не всегда дает правильные результаты. По этой причине в исходном тексте используется прямое указание на тип long вместо операции приведения.
304 Глава 8. Функция поддержки обработки изображения «ImageWidth» и «ImageHeight» — ограничители, используемые для проверки параметров, подходящих к функциям «GetPixelFromlmage» и «PutPixellnlmage». Любые попытки параметров обойти эти ограничители считаются ошибками. «ImageWidth» и «ImageHeight» — переменные величины, которые изменяются, когда PCX-файл считывается в память или отображается. Функции «GetPixelFromlmage» и «PutPixellnlmage» динамически определя- ют истинность параметров разрешения, которые они получают от только что считанного изображения. Они должны автоматически копировать изображения с разрешениями 320 х 200, 640 х 200 и 640 х 480. Проверка истинности параметров представлена на этом уровне для того, чтобы облегчить выявление типа ошибки путем проверки на каждом уровне программы. Таблица 8.1. Функции поддержки обработки изображения 1. Копирование изображения с одного буфера на другой. Прототип void CopyImage(BYTE huge*SourceBuf,BYTE huge*DestBuf); где «SourceBuf» и «DestBuf» — указатели типа huge на буферы изображе- ний. Действие Эта функция копирует изображение с разрешением 320 х 200 с одного буфе- ра на другой. Она служит для сохранения копии изображения, в то время как подлинное изображение подвергается некоторым преобразованиям. Та- ким образом, первоначальное и преобразованное изображения могут срав- ниваться. 2. Чтение определенного элемента изображения из буфера. Прототип BYTE GetPixelFromImage(BYTE huge*Image,unsigned Col,unsigned Row); где «Image» — указатель типа huge на буфер изображения. «Со1» и «Row» определяют, где в буфере изображения должен быть извле- чен байт данного изображения. Действие Эта функция читает байт данного изображения из буфера изображе- ния, определенного как «Image» с расположением «Со1» (колонка) и «Row»(ряд). Эта функция осуществляет проверку параметров, определен- ных «Со1» и «Row», для того чтобы убедиться, что они не выходят за пределы буфера «Image». Если любой параметр выпадает из диапазона, будет обнаружена ошибка и возвращен байт с нулевым значением. С це- лью увеличения производительности в тщательно отлаженной программе проверка входных параметров может быть удалена. До тех пор пока не отлажены программы обработки изображения, наверное, проверку лучше оставить. Значение байта, возвращенного функцией «GetPixelFromlmage», служит индексом в палитре VGA, с которой будет отображаться элемент изображения (подробности см. ниже). Эти функции используются всякий раз, когда необходимо прочитать данные изображения из буфера.
Введение 305 3. Запись определенного элемента изображения в буфер изо- бражения. Прототип CompletionCode PutPixelInImage(BYTE huge*Image,unsigned Col,unsigned Row, unsigned Color); где все параметры определены выше, кроме: Color — определяет значение интенсивности записываемого элемента изо- бражения. Действие Эта функция записывает байт данных изображения «Color» в буфер изо- бражения, определяемый как «Image» с расположением, определяемым значениями «Со1» и «Row», чтобы убедиться, что они не выходят за преде- лы буфера «Image». Если любой параметр выпадает из диапазона, ошибка будет найдена и FALSE будет возвращено. Как уже говорилось выше, в тща- тельно отлаженных программах параметры проверки могут быть удалены для увеличения производительности. «Color» — это индекс в палитру VGA цвета, с которой отображается эле- мент изображения. При 64-уровневой шкале яркости изображений «Color» будет находиться в диапазоне от 0 до 63. «Color», равный 0, — обычно чер- ный цвет, a «Color», равный 63, — обычно белый цвет. Для изображений, состоящих из 256 цветов, «Color» будет изменяться от 0 до 255. Для получе- ния буфера изображения, который содержит все возможные цвета, может быть вызвана функция «PutPixellnlmage» 256 раз, каждый раз наращивая значения «Color». 4. Запись горизонтальной линии в буфер изображения. Прототип CompletionCode DrawHLine(BYTE huge *Image, unsigned Col,unsigned Row, unsigned Length, unsigned Color); где все параметры определены выше. Действие Эта функция записывает горизонтальную линию элементов изображения, начинающуюся на «Со1» и «Row», длиной «Length» и цвета «Color» в буфер изображения, определяемый как «Image». При «Length», равной 0, имеет- ся единственный элемент изображения. При «Length», равной 1, — два элемента изображения и т. д. Эта функция также осуществляет проверку, обнаруживает ошибку и возвращает значение FALSE, если один из пара- метров выпадает из диапазона. Если все хорошо, TRUE вернется из этой функции. Данная функция используется, когда должны быть записаны горизонталь- ные линии в буфер изображения. Вместе с функцией «DrawVLine», кото- рая описана ниже, она может быть использована для прорисовки сетки на изображении. Обе функции также используются для отображения гисто- грамм, как будет показано ниже. 20-3
306 Глава 8. Функции поддержки обработки изображения Таблица 8.1. (Продолжение) 5. Запись вертикальной линии в буфер изображения. Прототип CompletionCode DrawVLine(BYTE huge *Image, unsigned Col,unsigned Row, unsigned Length, unsigned Color); где все параметры определены выше. Действие Действие этой функции аналогично действию функции «DrawHLine», по- дробно описанной выше. 6. Копирование части буфера изобраэюения в новый выходной буфер. Прототип void ReadImageAreaToBuf(BYTE huge *Image, unsigned Col,unsigned Row, unsigned Width, unsigned Height BYTE huge *Buf); где все параметры определены, кроме: «Buf» — указатель на буфер, с которого должна быть прочитана порция изобр ажения. Действие Эта функция используется для копирования части (или целого) изобра- жения, содержащегося в буфере изображения в выходной буфер, опреде- ляемый как «Buf». Размеры части копируемого изображения описывают- ся «Со1», «Row», «Width» и «Height». Копированные данные изображения формируются как упакованные биты в буфер без учета колонок и рядов исходного изображения. Манипуляции с данными изображения в буфере возможны до тех пор, пока содержатся в памяти новые соотношения ко- лонка/ряд. Эта функция используется, когда должна быть сохранена (для последую- щего восстановления) часть буферного изображения. Позже она будет ис- пользоваться для того, чтобы позволить гистограммам возникнуть на изо- бражении без потери данных изображения под гистограммой. Эта функция используется, чтобы вернуть данные, которые были сохранены, обратно в буфер изображения. 7. Копирование исходного буфера в буфер изображения. Прототип void WriteImageAreaFromBuf(BYTE huge *Buffer,unsigned BufWidth, unsig- ned BufHeight,BYTE huge *Image,unsigned ImageCol,unsigned ImageRow); где все параметры обозначены выше, кроме: «BufWidth» и «BufHeight» определяют размеры временного буфера, обозна- ченного как «Buffer», который будет записываться в буфер изображения, обозначенный «Image». «ImageCol» и «ImageRow» определяют, где в выходном изображении будет записано содержание буфера.
Введение 307 Действие «WritelmageAreaFromBuf» представляет собой дополнительную операцию к функции «ReadlmageAreaToBuf», описанной выше. Она используется для копирования исходного буфера, обозначенного «Buffer», в выходной буфер изображения, обозначенный «Image». «BufWidth» и «BufHeight» описыва- ют размеры исходного буфера. Исходный буфер копируется в буфер изо- бражения, начиная с «ImageCol» и «ImageRow». Вследствие того что поло- жение в буфере выходного изображения, в который копируется исходный буфер, определяется параметрами, тот же исходный буфер может копиро- ваться в буфер выходного изображения несколько раз. Другими словами, любая часть буфера изображения (которая считывается при вызове функ- ции «ReadlmageAreaToBuf») может быть возвращена в буфер изображе- ния в любое желаемое место. Эта функция используется для восстановле- ния части буфера изображения, предварительно сохраненной с помощью функции «ReadlmageAreaToBuf». Как уже говорилось, при выведении на экран гистограммы используют функцию «WritelmageAreaFromBuf», для того, чтобы восстановить изображение, лежащее под гистограммой. Другое использование этой функции заключается в изготовлении многочисленных копий объекта в буфере изображения. 8. Установка указанной части буфера изображения в указан- ное значение. Прототип void ClearImageArea(BYTE huge *Image, unsigned Col,unsigned Row, unsigned Width, unsigned Height, unsigned PixelValue); где все параметры определены выше, кроме: «PixelValue» определяет значение байта, записанного в каждой ячейке бу- фера. Действие Эта функция устанавливает часть буфера изображения, обозначенного «Image» и определяемого «Со1», «Row», « Width» и «Height» в установлен- ное значение элемента изображения. Параметр «PixelValue» является ана- логичным параметру «Color», описанному для функции «PutPixellnlmage». Если «PixelValue» имеет значение 0, определенная часть изображения бу- дет черной. Эта функция используется в подготовке заново размещенных буферов изображения. Она устанавливает данные в буфер изображения, равные постоянному значению, вместо того чтобы заполнять его хаотично распределенными данными. При установке элементов буфера изображе- ния в определенное значение степень преобразования любого изображения, помещенного в инициированный буфер, сразу становится очевидной. Эта функция может также быть использована как часть покадровых процес- сов, описанных ниже в этой части книги, для очистки части изображения, которая в последующем будет перекрыта другим изображением. 9. Выполнение проверки обозначенных параметров. Прототип CompletionCode PaxameterCheckOK(unsigned Col,unsigned Row, unsigned ColExtent, unsigned RowExtent, char *FunctionName); где все параметры определены выше. 20*
308 Глава 8. Функции поддержки обработки изображения Таблица 8.1. (Продолжение) Действие Эта функция подтверждает, полностью ли соответствует часть изображе- ния, определяемая «Со1», «Row», «ColExtent» (ширина) и «RowExtent» (высота) буферу изображения с разрешением 320 х 200. Если это так, то функция возвращает TRUE. Если это не так, обнаруживается ошибка в программе и выполнение программы прекращается до устранения ошибки (пока не обнаруживается параметр, вышедший из диапазона). Эта функция широко используется в программе обработки изображения, представленной ниже. Заключение Говорят, что нельзя воздвигнуть здания, не заложив сначала крепкий фунда- мент. Тот же принцип применим и к обработке изображения. Алгоритмы об- работки изображения сами по себе ничего не значат, если нет хорошего про- граммного обеспечения, на котором они будут строиться. Это как раз то, о чем мы говорили в этой главе. Функции поддержки осуществляют все возможности низкого уровня, требуемые для обработки изображения. Как любой хороший фундамент, их присутствие скоро будет забыто. Но вы должны понять, что без них невозможны никакие другие процессы самого высокого уровня.
Глава 9 Точечные процессы В главе рассмотрены следующие вопросы: • Точечные процессы. • Гистограммы и что они могут рассказать об изображении. • Использование таблиц преобразований для иллюстрации точечных про- цессов. • Применение различных функций точечных процессов к данным изобра- жения. Введение Точечные процессы — фундаментальные операции обработки изображения. Они очень просты и наиболее часто используются в алгоритмах обработки изображе- ния. Они полезны и сами по себе, и в сочетании с другими классами алгоритмов. Так как точечные процессы менее сложны, чем другие алгоритмы обработки изображения, мы естественно начнем наше обсуждение именно с них. Точечные процессы — это алгоритмы, которые изменяют значение элемента в изображении и основаны единственно на том, что элемент изображения — это ве- личина (и иногда его положение). Никакие другие значения элемента изображе- ния не включаются в преобразование. Индивидуальные элементы изображения заменяются новыми значениями, которые алгоритмически связаны с исходным значением элемента изображения. Как результат алгоритмических отношений между исходным и новым значением элемента изображения, точечные процессы могут в общем случае выполняться в обратном порядке. Алгоритмы точечных процессов сканируют изображение элемент за элементом, осуществляя преобра- зование элементов изображения. Если преобразование зависит только от исход- ного значения элемента изображения, этот процесс может быть осуществлен с помощью таблиц преобразований. Таблицы преобразований подробно рассмотре- ны ниже. Если преобразование в точечных процессах также учитывает и рас- положение элементов изображения, то для преобразования значений элементов изображения используется формула или формула в сочетании с таблицей пре- образований. В общем случае точечные процессы не изменяют пространственные соотношения в пределах изображения. Поэтому точечные процессы не могут мо- дифицировать детали, содержащиеся в изображении. Пожалуйста, запомните, что применение алгоритмов точечных процессов к изображениям имеет смысл только для полутонового изображения, значения яркости которого представляются значениями элементов изображения. Для ма- нипулирования цветными изображениями необходимо применить алгоритмы к каждому цвету отдельно, вместо того чтобы применять их прямо к значениям элементов изображения. Напомним, что для изображений, использующих 256 цветов, с разрешением 320 х 200, значение элементов изображения является ин- дексом к палитре VGA и не является истинной яркостью или RGB-значением.
310 Глава 9. Точечные процессы Применяя преобразование индекса, в вашем изображении вы можете полу- чить относительно специфические цвета. В этой главе рассматриваются следующие точечные процессы: * Просветление. * Негативные изображения. ♦ Пороговые изображения. ♦ Увеличение контраста изображения. * Псевдораскрашивание. Все алгоритмы точечных процессов показаны в листинге 9.1. Все прототи- пы функций, используемых в точечном процессе, содержатся в листинге 9.1 и описаны в табл. 9.1 и 9.2. Необходимо, чтобы этот файл был включен в про- грамму, которая использует функции точечных процессов. Файлы «pttest.c» и «pttest.prj», используемые в листинге 9.2, содержат образец программы, исполь- зуемой для тестирования функций точечной обработки. Этот пример програм- мы может служить как модель структуры типичной программы, использующей функции точечной обработки. Все тестовые изображения, описанные в этой гла- ве, получены с помощью этой программы. Листинг 9.1. Библиотека функций точечного процесса Ниже приводится содержимое файла ptprocess.h: /****************************************/ /♦ Header файл ♦/ /♦ Функции точечной обработки ♦/ /♦ разработан в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 11/07/89 ♦/ /****************************************/ extern unsigned HistograH[MAXQUAHTLEVELS]; /♦ Прототипы функций поддержки гистограмм ♦/ void InitializeLUT(BYTE «LookUpTable); void PtTransforH(BYTE huge «InageData, unsigned Col, unsigned Row, unsigned Width, unsigned Height, BYTE *LookUpTable); void GenHistogran(BYTE huge «InageData, unsigned Col, unsigned Row, unsigned Width, unsigned Height); void DisplayHist(BYTE huge elnageData, unsigned Col, unsigned Row, unsigned Width, unsigned Height);
Введение 311 /* Функции точечных преобразований */ void Adj iBageBrightness(BYTE huge elBageData» short BrightnessFactor, unsigned Col» unsigned Row» unsigned Width» unsigned Height); void HegatelaageCBYTE huge slBageData» unsigned Threshold» unsigned Col» unsigned Row» unsigned Width» unsigned Height) ; void Thresholdlaage(BYTE huge «iBageData» unsigned Threshold» unsigned Col» unsigned Row» unsigned Width» unsigned Height); void StretchlaageContrast (BYTE huge elBageData» unsigned «HistoData» unsigned Threshold» unsigned Col» unsigned Row» unsigned Width» unsigned Height) ; Далее следует содержимое файла ptprocess «с: /ее**************************************/ /* Функции точечной обработки */ /* разработаны в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли */ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 11/07/89 ♦/ /****************************************/ tindude <stdio.h> tindude <stdlib.h> tindude <conio.h> tindude <dos.h> tindude <alloc.h> tindude <process.h> tindude <graphics.h> tindude ">isc.h" tindude "pcx.h" tindude "vga.h" tindude "iaagesup.h" /* массив для хранения гистограммы ♦/ unsigned Hist ogrаж [MAXQUANTLEVELS] ; /♦ Функции таблицы преобразования (LUT) Инициализируем Look Up Table (LUT) для прямого отображения. Если точечная трансформация выполняется на инициализированной LUT, выходные данные равны входным. Эта функция обычно вызывается во время подготовки к кодификации в LUT. ♦/
312 Глава 9. Точечные процессы Листинг 9.1. (Продолжение) void InitializeLUT(BYTE *LookUpTable) register unsigned Index; for(Index-0;Index<MAXQUANTLEVELS;Index**) LookUpTable [Index] -Index; 1 /♦ Эта функция выполняет точечное преобразование части изображения, определяемое параметрами Col, Row, Width и Height. Действительное преобразование записано в Look Up Table, чей адрес передается в качестве параметра. ♦/ void PtTransforn(BYTE huge *InageData, unsigned Col, unsigned Row, unsigned Width, unsigned Height, BYTE «LookUpTable) register unsigned InageCol, InageRow; register unsigned ColExtent, RowExtent; ColExtent-Col+Width; RowExtent-Row+Height; if (ParaneterCheckOK (Col, Row, ColExtent, RowExtent, "PtTransf om” ) ) f or (InageRow-Row; InageRow<RowExtent; Inage Ro w++) f or (InageCol-Col; InageCoKColExtent; InageCol**!*) PutPixellnlnage (Inage Data, InageCol, InageRow, LookUpTable[GetPixelFronInage(InageData,InageCol,InageRow)]); 1 /♦ начало функций гистограмм Эта функция вычисляет гистограмму лпбой части изображения. ♦/ void GenHistogran(BYTE huge «InageData, unsigned Col, unsigned Row, unsigned Width, unsigned Height) register unsigned InageRow, InageCol, RowExtent, ColExtent; register unsigned Index; /♦ очистка массива гистограммы ♦/ for (Index-0; Index<MAXQUAMTLEVELS; Index**) Hist ogran[Index]-0; RowExtent-Row*Height; ColExtent-Col+Width; if (ParaneterCheckOK(Col,Row,ColExtent,RowExtent,"GenHistogran")) < /♦ расчет гистограммы ♦/ for (InageRow-Row; InageRow<RowExtent; InageRow**)
Введение 313 f or (InageCol»Col; ImageCoK ColExtent; ImageCol++) Histogram [GetPixelFromlmage (ImageData, ImageCol, ImageRow)]+=l; } /♦ Эта функция вычисляет и отображает гистограмму изображения или части изображения. При вызове предполагается, что адаптер VGA уже находится в режиме 13 hex. void DisplayHist(BYTE huge «InageData, unsigned Col, unsigned Rom, unsigned Width, unsigned Height) { BYTE huge «Buffer; register unsigned Index,LineLength,XPos,YPos; unsigned MaxRepeat; /* Размещение памяти для сохранения изображения под гистограммой */ Buffer » (BYTE huge ♦)farcalloc((long)HISTOWIDTH*HISTOHEIGHT,sizeof(BYTE)); if (Buffer == NULL) { printf ("No buffer мемогу\п") ; exit(ENoMemory); } /♦ Сохранение копии изображения */ ReadlmageAreaToBuf (InageData, HISTOCOL,HISTOROW,HISTOWIDTH,HISTOHEIGHT, Buffer); /♦ Устанавливаем регистр цвета VGA 65 в красный, 66 - в зеленый и 67 - в синий с тем, чтобы гистограмма визуально отличалась от полутонового изображения. ♦/ SetAColorReg(65,63,0,0); SetAColorReg(66,0,63,0); SetAColorReg(65,0,0,63); /* Расчет гистограммы для изображения ♦/ GenHistogram(ImageData, Col, Rom , Width,Height) ; MaxRepeat=0; /♦ Находим наиболее повторяпцееся значение элемента изображения. Оно будет использовано для масштабирования. ♦/ f or (Index»0; Index<MAXQUANTLEVELS; Index++) MaxRepeat» (Hist ogr am [Index] >MaxRepeat) ? Histogram[Index]:MaxRepeat; /* Закрашиваем фон гистограммы */ ClearImageAreadmageData,HISTOCOL,HISTOROW,HISTOWIDTH,HISTOHEIGHT, 67) ;
314 Глава 9. Точечные процессы Листинг 9.1. (Продолжение) /* Рисуем границу прямоугольника, ограничивавшего гистограмму ♦/ DrawVLine(InageData,HISTOCOL,HISTOROW,HISTOHEIGHT-l.BLACK); DravVLine(lBageData,HISTOCOL+HISTOWIDTH-l,HISTOROW,HISTOHEIGHT-1.BLACK); DrawHLine(lBageData,HISTOCOL,HISTOROW+HISTOHEIGHT-l.HISTOWIDTH-l.BLACK); DrawHLine(IBageData,HISTOCOL,HISTOROW,HISTOWIDTH-1.BLACK); /* Базовая линия */ DrawHLine(IBageData,AXISCOL.AXISROW.AXISLENGTH.WHITE); DrawHLine(IBageData.AXISCOL.AXISROW+1,AXISLENGTH.WHITE); /* Теперь делаем реальное отображение гистограммы в буфер изображения. */ for(Index-О; Index<MAXQUANTLEVELS; Index++) { LineLength - (unsigned)(((long)Histogran[Index] * MAXDEFLECTION) / (long) MaxRepeat); XPos " DATACOL + Index*2; YPos - DATAROW - LineLength; DrawVLine(InageData,XPos,YPos,LineLength.66); /♦ Отображаеи изображение с наложенной гистограммой. */ DisplayInagelnBuf(InageData,NOVGAINIT,WAITFORKEY); /* After display, restore isage data under histograa */ WritelMageAreaFronBuf(Buffer.HISTOWIDTH.HISTOHEIGHT,InageData. HISTOCOL.HISTOROW); farfree((BYTE far *)Buffer); /* Различные функции точечной трансформации */ void AdjIBageBrightness(BYTE huge «IBageData,short BrightnessFactor, unsigned Col, unsigned Row, unsigned Width, unsigned Height) { register unsigned Index; register short NewLevel; BYTE LookUpTable DUXQUANTLEVELS]; for(Index - MINSAMPLEVAL;Index < MAXQUANTLEVELS;Index++) NewLevel - Index + BrightnessFactor; NewLevel - (NewLevel < MINSAMPLEVAL) ? MINSAMPLEVAL : NewLevel; NewLevel - (NewLevel > MAXSAMPLEVAL) ? MAXSAMPLEVAL : NewLevel; LookUpTable[Index] - NewLevel; PtTransf omdnageData, Col, Row, Width, Height, LookUpTable);
Введение 315 /♦ Эта функция сделает негативным изображение точку за точкой. Порогом является значение данных изображения, где начинается негатив. Если порог » 0, все значения точек становятся негативными. То есть, значение 63 становится 0 и значение 0 становится 63. Если порог больше 0, точки в диапазоне 0.. порог-1 не трансформируются, а точки порог. .63 становятся негативными. */ void NegateImage(BYTE huge ♦ImageData, unsigned Threshold, unsigned Col, unsigned Rom, unsigned Width, unsigned Height) { register unsigned Index; BYTE LookUpTable[MAXQUANTLEVELS]; /* Проходим через отображение ♦/ InitializeLUT(LookUpTable); /♦На основании значения порога трансформируем элемент в LUT ♦/ for (Index - Threshold; Index < MAXQUANTLEVELS; Index++) LookUpTable[Index] - MAXSAMPLEVAL - Index; PtTransform (I mage Data, Col, Rom , Width, Height, LookUpTable ); } /♦ Эта функция преобразует серое изображение в бинарное путем установки каждой точки on (белая) или off (черная). Значение точки, в которой происходит разделение, определяется значением порога. Точки в диапазоне 0..порог-1 становятся черными, а точки порог..63 - белыми. */ void Thresholdimage (BYTE huge ♦ImageData,unsigned Threshold, unsigned Col,unsigned Rom, unsigned Width,unsigned Height) { register unsigned Index; BYTE LookUpTable[MAXQUANTLEVELS]; for (Index « MINSAMPLEVAL; Index < Threshold; Index++) LookUpTable[Index] » BLACK; for (Index - Threshold; Index < MAXQUANTLEVELS; Index++) LookUpTable[Index] » WHITE; PtTransform(ImageData,Col,Rom,Width,Height,LookUpTable); }
316 Глава 9. Точечные процессы Листинг 9.1. (Продолжение) void StretchliiageContrast(BYTE huge «InageData, unsigned «HistoData, unsigned Threshold, unsigned Col, unsigned Roh, unsigned Vidth, unsigned Height) register unsigned Index,NevMin,NevMax; double StepSiz,StepVal; BYTE LookUpTable[MAXQUANTLEVELS]; /♦ Поиск (начиная с младшего бита по направлению к старшему) бита, превосходящего порог ♦/ for(lndex-0; Index < MAXQUANTLEVELS; Index++) if(HistoData[Index] > Threshold) break; NewMin = Index; /♦ Поиск (начиная co старшего бита по направлению к младшему) бита, превосходящего порог ♦/ for(Index3MAXSAMPLEVAL;Index > NevMin; Index—) if (HistoData [Index] > Threshold) break; NevMax = Index; StepSiz = (double)MAXQUANTLEVELS/(double)(NevMax-NevMin+1) ; StepVal a 0.0; /♦ значения ниже нового минимума получают значение 0 в LUT ♦/ for(lndex=0; Index < NewMin; Index++) LookUpTable[Index]-MINSAMPLEVAL; /♦ значения выше нового максимума получают максимальное значение ♦/ for(Index=NevMax+l; Index < MAXQUANTLEVELS; Index++) LookUpTable[Index]“MAXSAMPLEVAL; /♦ значения между новым минимумом и новым максимумом растягиваются */ for(IndexaNevMin; Index <я NevMax; Index++) LookUpTable [Index] aStepVal; StepVal+aStepSiz; 1 /♦ Таблица LUT подготовлена к точечной трансформации данных изображения. ♦/ PtTransfora(InageData, Col,Row, Vidth, Height, LookUpTable) ;
Введение 317 Листинг 9.2. Пример программы точечного процесса Ниже приводится текст файла pttest.prj: graphics.lib vgagraph.obj egavga.obj рсх (misc.h pcx.h) vga (mics.h vga.h pcx.h) imagesup (mics.h vga.h psx.h image sup. h) ptprocess (mics.h vga.h psx.h imagesup.h ptprocess.h) pttest (mics.h vga.h psx.h imagesup.h ptprocess.h) Далее следует текст файла pttest.с: /* Демонстрационная программа ♦/ /♦ точечных методов обработки изображения ♦/ /♦ разработана в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли ♦/ /♦ /♦ ♦/ ♦/ Версия: 1.0 /♦ Последние изменения внесены 12/26/89 ♦/ /******************************************/ ♦include <stdio.h> ♦include <alloc.h> ♦include <process.h> ♦include <graphics.h> ♦include "misc.h" ♦include "pcx.h" ♦include "vga.h" ♦include "imagesup.h" ♦include "ptprocess.h" /♦ Основная программа точечного процесса ♦/ void main(void) { char «InFileNamel = "p2ch9ila"; char *InFileName2 « "p2ch9i2a"; char ♦TnFileName3 = "p2ch9i3a"; char *InFileName4a « "p2ch9i4a"; char *InFileName4b = "p2ch9i4c"; char *InFileName4c « "p2ch9i4e"; char «InFileNameS » "p2ch9i5a"; char *InFileName6 « "p2ch9i6a";
318 Глава 9. Точечные процессы Листинг 9.2. (Продолжение) BYTE huge *TheInage; unsigned GenPCXFiles ж FALSE; /* Управляем созданием выходных файлов »/ BYTE PseudoColorLUT[MAXQUAMTLEVELS]; InitGraphicsO; printf("Point Transform Exanple Prograa\n\n"); printf("Reading the Inage PCX File into nenory\n"; /* Создаем изображение 9.3 - Просветление ♦/ /♦ Загрузим PCX файл в память ♦/ if(ReadPCXFileToBuf(InFileManel,ATheInage) !> NoError) exit(l); /• вывод изображения на экран, указанного TheInage »/ DisplayInagelnBuf(Thelnage,INITVGALOADPALETTE,WAITFORKEY); DisplayHist(TheInage,MINCOLNUM,MIMROWMUM,130,MAXROWS); if(GenPCXFiles) WritePCXFile("p2ch9ilb",8,320,200,1,320); AdjIBageBrightness(Thelnage,+17,MINCOUTOM,MIMROWNUM,130,MAXROWS); DisplayInagelnBuf(Thelnage,MOVGAIMIT.WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2ch9i1c",8,320,200,1,320); DisplayHist(Thelnage,MINCOLNUM,MINROWNUM, 130 .MAXROWS) ; if(GenPCXFiles) WritePCXFile("p2ch9ild",8,320,200,1,320); farfree((BYTE far *)TheInage); /• Создаем изображение 9.4 - Негативное изображение*/ /* Загрузим PCX-файл в память */ if(ReadPCXFileToBuf(InFileNane2,MheInage) != MoError) exit(l); /« вывод изображения на экран, указанного Thelnage */ DisplayInagelnBuf(Thelnage,MOVGAIMIT,WAITFORKEY); Megatelnage(Thelnage,0,MIMCOLNUM,MIMROWMUM,MAXCOLS,MAXROWS); DisplayInagelnBuf(Thelnage,MOVGAIMIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2ch9i2b",8,320,200,1,320); farfree((BYTE far *)TheInage); /* Перезагрузим файл PCX в память */ if(ReadPCXFileToBuf(InFileNane2,RTheInage) !> NoError) exit(l); /• вывод изображения на экран, указанного Thelnage */ DisplaylnagelnBuf(Thelnage,MOVGAIMIT,WAITFORKEY);
Введение 319 Negatelmage(Thelmage ,50 ,MINC0LNUM,MINR0WNUM,MAXCOLS,MAXROWS) ; DisplayImagelnBuf(Thelmage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile(Hp2ch9i2c",8,320,200,1,320); farfree((BYTE far ♦)TheImage); /♦ Создаем изображение 9.5 - Пороговое изображение */ /♦ Загрузим-PCX файл в память */ if (ReadPCXFileToBuf(InFileName3, tTheImage) ! = NoError) exit(l); /* вывод изображения на экран, указанного Thelmage */ Display InageinBuf(Thelmage,NOVGAINIT,WAITFORKEY); Thresholdimage(Thelmage,35,110,50,105,105); DisplayImagelnBuf(Thelmage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile(”p2ch9i3b”,8,320,200,1,320); farfree((BYTE far *)TheImage); /* Создаем изображение 9.6 - Изучение контрастов */ /♦ Загрузим РСХ-файл в память ♦/ if (ReadPCXFileToBuf(InFileName4a,tTheImage) != NoError) exit(l); /♦ вывод изображения на экран, указанного Thelmage */ DisplayImagelnBuf(Thelmage,NOVGAINIT,WAITFORKEY); DisplayHist (Thelmage ,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS) ; if(GenPCXFiles) WritePCXFile(”p2ch9i4b”,8,320,200,1,320); 9 farfree((BYTE far *)TheImage); /♦ Загрузим РСХ-файл в память ♦/ if (ReadPCXFileToBuf (InFileName4b, tThelmage) ! = NoError) exit(l); /* вывод изображения на экран, указанного Thelmage */ DisplayImagelnBuf(Thelmage,NOVGAINIT,WAITFORKEY); DisplayHist(Thelmage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS) ; if(GenPCXFiles) WritePCXFile("p2ch9i4d",8,320,200,1,320); farfree((BYTE far *)TheImage); /♦ Загрузим РСХ-файл в память */ if (ReadPCXFileToBuf (InFileName4c, tThelmage) ! = NoError ) exit(l); /♦ вывод изображения на экран, указанного Thelmage */ DisplayImagelnBuf (Thelmage, NOVGAINIT,WAITFORKEY) ;
320 Глава 9. Точечные процессы Листинг 9.2. (Продолжение) DisplayHist(Thelmage, MINCOLNUM, MINROWNUM, MAXCOLS,MAXROWS) ; if(GenPCXFiles) WritePCXFile("p2ch9i4f ",8,320,200,1,320); farfree((BYTE far *)TheImage); /♦ Создаем изображение 9.7 - Увеличение контраста изображения ♦/ /♦ Загрузим PCX файл в память */ if (ReadPCXFileToBuf (InFileName5,tTheImage) !- NoError) exit(l); /♦ вывод изображения на экран, указанного Thelmage */ DisplayImagelnBuf (Thelmage, NOVGAINIT,WAITFORKEY) ; DisplayHist(TheImage, MINCOLNUM, MINROWNUM, MAXCOLS, MAXROWS) ; if(GenPCXFiles) WritePCXFile("p2ch9i5b",8,320,200,1,320); Stret chlmageContrast (Thelmage, Histogram,475, MINCOLNUM, MINROWNUM, MAXCOLS,MAXROWS); DisplayImagelnBuf(Thelmage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2ch9i5c”,8,320,200,1,320); DisplayHist(Thelmage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS) ; if(GenPCXFiles) WritePCXFile ("p2ch9i5d",8,320,200,1,320); farfree((BYTE far *)TheImage); /* Создаем изображение 9.8 - Псевдораскрашивание ♦/ /♦ Загрузим PCX файл в память */ if (ReadPCXFileToBuf (InFileName6,tTheImage) !я NoError) exit(l); /* вывод изображения на экран, указанного Thelmage */ DisplayImagelnBuf (Thelmage,NOVGAINIT,WAITFORKEY) ; /* Установим цветовой регистр 64 на красный, 65 - на зеленый, 66 - на синий. ♦/ SetAColorReg(64,63,0,0); SetAColorReg(65,0,63,0); SetAColorReg(66,0,0,63); /♦ Вызов функции InitializeLUT устанавливается через преобразование для LUT. Будут преобразованы только те значения, которые необходимо изменить из-за их значений по умолчанию. ♦/ InitializeLUT (PseudoColorLUT); /♦ Присвоить цветовой регистр 64 для элементов изображения со значениями 60 - 63 ♦/
Гистограммы 321 PseudoColorLUT([63] - 64; PseudoColorLUT([62] - 64; PseudoColorLUT([61] - 64; PseudoColorLUT([60] - 64; /* Присвоить цветовой регистр 64 56 - 59 ♦/ PseudoColorLUT([59] - 65; PseudoColorLUT([58] - 65; PseudoColorLUT([57] - 65; PseudoColorLUT([56] “ 65; /♦ Присвоить цветовой регистр 64 52 - 55 */ PseudoColorLUT([55] « 66; PseudoColorLUT([54] - 66; PseudoColorLUT([53] “ 66; PseudoColorLUTt[52] - 66; для элеиентов изображения co значениями для элеиентов изображения со значениями /» В этой точке LUT полностью инициирована. Далее применяем преобразование к данным изображения. ♦/ PtTransfornCThelnage,MINCOLNUM,MINROWNUM,MAXCOLS.MAXROWS, PseudoColorLUT); /♦ Выводим на экран псевдораскраиенное изображение */ DisplayInageinBuf(Thelnage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2ch9i6b",8,320.200,1,320); farfree((BYTE far »)TheInage); restorecrtnodeO; closegraphO; Гистограммы Гистограмма — диаграмма распределения значений интенсивности элементов в изображении. Гистограммы могут свидетельствовать об общей яркости и контра- сте изображений. Динамический диапазон значений элементов, которые обра- зуют изображение, очевиден. Таким образом, гистограммы являются ценным методом для процесса как количественной, так и качественной обработки изо- бражения. Типичная гистограмма — это двумерный график случайных чисел элементов изображения в зависимости от значений элементов. На рис. 9.1 показана типич- ная гистограмма. Этот формат гистограммы будет использован на протяжении 21-3
322 Глава 9. Точечные процессы Рис. 9.1. Гистограмма интенсивности точек. Примечания 1. Вертикальная ось масштабирована так, чтобы соответствовать максимальному зна- чению. (Значение элементов изображения равно 33.) Все другие значения масштабиро- ваны относительно этого максимального значения. 2. Суммарное значение интенсивности х случайность появления = 64000 для разреше- ния изображения 320 х 200. 3. Данная гистограмма не отражает какого-либо реального изображения. всей книги. Хотя гистограмма, показанная на рисунке, была изготовлена спе- циально для нашего рассмотрения, она отражает ту информацию, которую мы можем узнать об изображении. Например, показано, что целый диапазон воз- можных значений элементов изображения полностью не использован. Значений элементов изображения от 42 до 62 не существует. Если бы это была гисто- грамма реального изображения, то внешний вид этого изображения мог бы быть улучшен с помощью точечного процесса, называемого увеличением контраст- ности, Увеличение контрастности изображения преобразует его таким образом, что оно будет занимать весь диапазон значений элементов изображения. Обычно это проявляется в более качественном изображении. Гистограммы не попадают ни в одну из четырех категорий алгоритмов об- работки изображения, описанных во введении в эту часть книги. Гистограммы описываются в этой главе потому, что они являются очень полезным методом для использования и контроля за результатами точечных процессов. Для осуществления поддержки гистограмм используются две функции и структуры данных в библиотеке функций обработки изображения. Они описаны
Гистограммы 323 в табл. 9.1. В этой части книги можно увидеть много графиков гистограмм. Все они выглядят так же, как и гистограмма на рисунке 9.1. Таблица 9.1. Функции гистограмм 1. Структура данных гистограмм. Прототип unsigned Histogram[MAXQUANTLEVELS]; Действие Структуры данных используются для подсчета случаев каждого возмож- ного появления значения элемента изображения. В нашем случае (полуто- новые изображения) имеются 64 возможных значения элементов изобра- жения в диапазоне от 0 до 63. Массив беззнаковых целых используется для того, чтобы следить за счетом, потому что он позволяет использовать значения счета до 65535. Массив должен иметь тип long, если гистограм- мы выполнены на изображении с разрешением более чем 320 х 200. Если это так и гистограммы выполнены на полностью черном изображении и с разрешением 320 х 200 (все значения элементов изображения равны нулю), то счетчик нулевого значения элемента изображения будет иметь значе- ние, равное 64000. Все другие позиции в гистограмме, конечно, будут иметь значение, равное нулю. 2. Создание данных гистограммы. Прототип void GenHistogram(BYTE huge *ImageData, unsigned Col,unsigned Row, unsigned Width, unsigned Height); Действие Данная функция, во-первых, очищает каждый элемент в массиве гисто- граммы. Это необходимо, так как значения элементов изображения начи- наются с нулевого отсчета. Во-вторых, эта функция пересекает часть изо- бражения, указанную «ImageData» и определенную параметрами «Со1», «Row», «Width» и «Height». Каждое значение элемента изображения ана- лизируется в процессе пересечения, и его значение используется как индекс к массиву гистограммы для счета; содержимое ячейки масси- ва должно увеличиваться. Эта функция возвращается, когда табулиру- ется каждый элемент определенной части изображения. Данная функ- ция может быть вызвана отдельно, так как каждая точка процесса требует данных гистограммы, но выводить на экран всю гистограмму не нужно. Эта функция должна вызываться, когда требуется созда- ние и вывод на экран гистограммы. (Замечание: Эта функция изменя- ет рассматриваемый в целом массив «Гистограмм» в процессе ее дей- ствия.) 3. Создание и вывод гистограмм на экран. Прототип void DisplayHist(BYTE huge *ImageData, unsigned Col,unsigned Row, unsigned Width, unsigned Height); 21*
324 Глава 9. Точечные процессы Таблица 9.1. (Продолжение) Действие Эта функция рассчитывает и выводит гистограммы на экран для изо- бражения или для части изображения, определенной параметрами. Она использует почти все функции поддержки обработки изображения, опи- санные выше, и является хорошим примером того, как могут быть ис- пользованы функции поддержки. График гистограмм промасштабирован таким образом, чтобы соответствовать закрепленной части изображения. Границы этой части контролируются с помощью определений в файле imagesup.h. График гистограммы лежит в буфере изображения, в той его части, где ранее располагалась данная часть изображения. По этой причине часть изображения под гистограммой выделяют в другой бу- фер во время демонстрации гистограммы с последующим восстановле- нием в буфере изображения после прерывания демонстрации гистограм- мы. Таким образом, изображение остается неизмененным. Программа этой функции, которая используется для демонстрации гистограммы, может быть модифицирована для того, чтобы выводить гистограммы прямо на экран VGA, а не в буфер изображения. Это могут попробовать осуще- ствить сами читатели этой книги. Данная функция также изменяет рас- сматриваемый в целом массив «Гистограмм» в процессе ее функциониро- вания. Таблицы преобразований Таблицы преобразований являются настолько удобным методом для алгоритмов точечной обработки, что многие фирмы используют таблицы преобразований во время производства оборудования обработки изображения вместе с программ- ным обеспечением. При использовании таблиц преобразований максимально уве- личивается скорость выполнения алгоритмов обработки изображения. У нас нет аппаратных средств с таблицами преобразований, чтобы поиграть ими, но мы можем их эмулировать, используя массивы в программе. Идея та же. Потеря производительности, которую мы платим за эмуляцию таблицы преобразования программными средствами, зависит от скорости функционирования массивов, полученных с помощью используемого компилятора. Программное обеспечение таблиц преобразований использует входное значе- ние элемента изображения как индекс в массиве для того, чтобы вернуть но- вое используемое значение вместо исходного значения элемента изображения. Массив, конечно, будет предварительно заполняться значениями, которые отраг жают преобразования, выполненные на данных изображения, проходящих через таблицу преобразований. Для таблиц преобразований в оборудовании будут ис- пользованы отдельные ячейки ОЗУ для каждого возможного значения элемен- та изображения. Входное значение элемента изображения будет использоваться как адрес в ОЗУ, вызывая возврат содержания ОЗУ. ОЗУ также будет запол- няться преобразованными данными. В нашем случае мы будем обозначать таблицы преобразований следующим образом: BYTE LookUpTable[MAXQUANTLEVELS];
Таблицы преобразований 325 Ячейка Содержание таблицы таблицы преобразования преобразований 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 55 55 56 56 57 57 58 58 59 59 60 60 61 61 62 62 63 63 Размер байта Рис. 9.2. Инициирование таблиц преобразований. Если значение элемента изображения используется как индекс к инициированию та- блиц преобразований, то значение, взятое из массива, является тем же значением, что и индекс. Если PixelValue = 56, то LUT[PixelValue] = 56. Как уже говорилось выше, массив использует байт для хранения каждого возможного значения элемента изображения. Значение элемента изображения как индекса выходного значения — это значение, содержащееся в этом индексе в таблице преобразований. Все таблицы преобразований должны быть иниции- рованы перед их использованием для осуществления преобразования элементов изображения. Иначе говоря, данные, пришедшие из таблиц преобразований, не имеют определенного отношения к данным, вошедшим в таблицу преобразова- ний. Самым простым инициированием таблицы преобразований, которое мы бу- дет использовать, является помещение значения его индекса в каждую ячейку таблицы преобразований. Так, например, нулевая ячейка загружается нулем, ячейка с индексом, равным единице, — единицей и так до ячейки с индексом 63, которая загружается значением 63. Этот тип инициирования выполнен в ви- де функции поддержки таблицы преобразований «InitializeLUT». Рассмотрите внимательно рис. 9.2.
326 Глава 9. Точечные процессы Рис. 9.3. Просветление. а — исходное изображение; б— просветленное изображение; в — исходное изображение с гистограммой; г — просветленное изображение с гистограммой. В инициированных таким образом таблицах преобразований выходные дан- ные из таблиц преобразований те же, что и входные. Другими словами, пре- образования, примененные к данным, являются линейными. Другая функция таблицы преобразований «PtTransform» применяет преобразования, содержащи- еся в таблицах преобразований, к изображению или обозначенной части изобра- жения. Если функция «PtTransform» применяется для преобразования изобра- жения, используя только что инициированные таблицы преобразований, резуль- тат будет нулевым. Изображение не будет меняться. Конечно, обычно таблицы
Просветление 327 преобразований не демонстрируют нулевых преобразований. Наоборот, таблицы преобразований будут инициироваться с помощью передаточных функций, чтобы можно было применить их к данным изображения. Различные преобразователь- ные и передаточные функции являются темой следующего обсуждения. Просветление Иногда изображение может быть улучшено с помощью регулировки яркости. Просветление — это точечный процесс, который прибавляет (или вычитает) по- стоянные значения к (или от) элементам в изображении. Алгебраически элемент изображения со значением интенсивности V может быть записан следующим образом: V = V + b, где Ъ — постоянная яркости, которая может быть либо положительной, либо отрицательной. Если Ъ положительна, то яркость элементов изображения воз- растает, если Ъ отрицательна, яркость уменьшается. На рис. 9.3 показан эффект просветления левой части изображения с постоянным фактором +17. Гистограм- мы исходного и просветленного изображения также показаны в этих изображе- ниях. Обратите внимание, как гистограмма в просветленном изображении (рис. 9.3,г), соотносится с гистограммой, показанной на рис. 9.3,б. Эффект просве- тления изображения с помощью постоянной яркости 17 вызывает скольжение гистограммы вправо по направлению к самым высоким значениям элементов изображения. Расстояние скольжения равно постоянной яркости, используемой для просветления изображения, в данном случае 17. Теперь вы понимаете, по- чему в некоторых книгах по обработке изображения регулировка яркости изо- бражения может также быть названа скольжением гистограмм. Таблица 9.2. Библиотека функций точечного процесса 1. Инициирование массива таблицы преобразований. Прототип void InitializeLUT(BYTE *LookUpTable); Действие Эта функция инициирует таблицу преобразования входных данных в вы- ходные, т. е. величина, помещенная в ячейку в таблице преобразований является индексом этой ячейки. 2. Выполнение преобразование элемента на изображении с по- мощью таблицы преобразований. Прототип void PtTransform(BYTE huge *ImageData, unsigned Col,unsigned Row, unsigned Width, unsigned Height, BYTE huge *LookUpTable);
328 Глава 9. Точечные процессы Таблица 9.2. (Продолжение) Действие Эта функция применяет преобразование, которое предварительно запро- граммировано в массиве «LookUpTable», к изображению или к части изо- бражения, обозначенного как «ImageData» с определенными «Со1», «Row», «Width» и «Height». Она используется во всех алгоритмах точечных про- цессов, описанных в этой части. 3. Расчет гистограммы изображения. Прототип void GenHistogram(BYTE huge *ImageData, unsigned Col, unsigned Row, unsigned Width, unsigned Height); Действие Эта функция проходит изображение или часть изображения, обозначен- ное как «ImageData» с определенными «Со1», «Row», «Width» и «Height» и рассчитывает значения ячеек каждого элемента изображения. Рассма- триваемый в целом массив «Histogram» содержит частотное распределение данных гистограммы. 4. Расчет и демонстрация гистограммы изображения. Прототип void DisplayHist(BYTE huge *ImageData, unsigned Col, unsigned Row, unsig- ned Width, unsigned Height); Действие Эта функция аналогична функции «GenHistogram», за исключением того, что данные результирующей гистограммы заносятся в буфер «ImageData». Она предполагает, что дисплей VGA находится в режиме 13Н перед вызо- вом. Для дополнительной информации смотрите листинг 9.2 и текст. 5. Регулировка яркости изображения. Прототип void AdjImageBrightness(BYTE huge *ImageData, short BrightnessFactor, unsigned Col, unsigned Row, unsigned Width, unsigned Height); Действие Эта функция проходит изображение или часть его, обозначенные как «ImageData» с определенными «Со1», «Row», «Width» и «Height», и доба- вляет фактор «BrightnessFactor» к каждому элементу изображения. Фак- тор «BrightnessFactor» может быть положительным или отрицательным. Положительные значения этого фактора осветляют изображение, а отри- цательные значения делают его более темным. 6. Негативное изображение. Прототип void NegateImage(BYTE huge *ImageData, unsigned Threshold, unsigned Col, unsigned Row, unsigned Width, unsigned Height);
Просветление 329 Действие Эта функция проходит изображение или часть его, обозначенные как «ImageData» с определенными «Со1», «Row», «Width», и «Height» и отри- цает любые значения элементов изображения выше определенного порога. Отрицание элемента изображения означает вычитание его значения из мак- симального значения выборки «MAXSAMPLEVAL»(63). Применение этого алгоритма создает изображение, которое напоминает негативный снимок. 7. Пороговое изображение. Прототип void ThresholdImage(BYTE huge *ImageData, unsigned Threshold, unsigned Col, unsigned Row, unsigned Width, unsigned Height); Действие Эта функция проходит изображение или часть его, обозначенное как «ImageData» с определенными «Со1», «Row», «Width», и «Height» и уста- навливает на черное любой элемент изображения со значением от 0 до «Threshold-1» и на белое все элементы изображения со значением, более или равным «Threshold». Эффект применения алгоритма заключается в преобразовании полутонового изображения в черно-белое. Алгоритм этого порогового процесса часто используется (см. текст). 8. Увеличение контраста изображения. Прототип void StretchImageContrast(BYTE huge *ImageData, unsigned *HistoData, unsigned Threshold, unsigned Col, unsigned Row, unsigned Width, unsigned Height); Действие Используя данные гистограмм (ранее полученные из «ImageData»), содер- жащиеся в массиве «HistoData», эта функция пытается увеличить контраст изображения, указанного в «ImageData». Алгоритм, использованный для увеличения контраста, обсуждается подробно в тексте. Применить коррекцию яркости изображения так же легко, как и показанные формулы к каждому элементу изображения. Более удобный путь осуществле- ния коррекции яркости — использование таблицы преобразований функции «PtTransform», описанной выше. Преобразование яркости вычисляется и поме- щается в таблицу преобразований, а затем вызывается функция «PtTransform» для изменения данных изображения. Функция в программе точечного процес- са (листинг 9.1) «AdjlmageBrightness» использует это. Единственное осложне- ние, которое следует учесть, это переполнение элемента изображения или его отрицательное значение. Если, например, положительное значение коррекции яркости добавляется к значению элемента изображения и результат превышает максимально возможное значение, равное 63 («MAXSAMPLEVAL»), то возника- ют условия переполнения. Кроме того, если отрицательная яркость добавляется
330 Глава 9. Точечные процессы к малому значению элемента изображения, то результат может стать отрица- тельным. Это называется отрицательностью значения элемента изображения. По этим причинам фрагмент программы, расположенный в функции «Adjimage Brightness», удерживает значения, находящиеся в таблице преобразований в диа- пазоне от 0 до 63. Если вход в таблицу преобразований после коррекции яркости превышает максимальное значение, равное 63, он устанавливается равным 63. Если вход в таблицу преобразований будет отрицательным, он устанавливает- ся равным 0. Установка таблицы преобразований таким образом гарантирует, что все элементы изображения, преобразованные с помощью этих таблиц, будут находиться в определенных пределах. Отсюда можно заключить, что использование таблицы в таких простых пре- образованиях слишком нерентабельно. Возможно, это верно. Гораздо скорее вы- звать байты данных прямо из изображения, дополнить их постоянной яркости и поместить их обратно, чем инициировать массив таблицы преобразований с по- мощью передаточной функции и затем пропустить через нее каждое значение элемента изображения. Элегантность преобразований, основанных на таблицах преобразований, становится очевидной только в случаях с более сложными пере- даточными функциями. Эффективность преобразований, основанная на таблице преобразований, может быть увеличена путем предварительного расчета таблиц преобразований и сохранения их в памяти до тех пор, пока они не понадобятся. Производительность преобразований с использованием предварительно рассчи- танной таблицы преобразований при дальнейшем применении к изображению будет постоянной, не считая сложности передаточной функции. Это происходит оттого, что все основные вычисления были выполнены в процессе инициирования таблиц преобразований. Другим преимуществом использования таблиц преобразований в точечных процессах является последовательная структура результирующих алгоритмов. Рассмотрим функции «Negatelmage», «Thresholdlmage» и «Stretchimage Contrast» в дополнение к функции «AdjlmageBrightness». Заметили ли вы, как они похожи? Если вы один раз поймете, как закодирован алгоритм и работает таблица преобразований, вы никогда уже не вернетесь к старым методам. Негативные изображения Негативные изображения, напоминающие негативные фотоснимки, можно легко получать с помощью точечных преобразований. Идея заключается в том, чтобы сделать часть изображения, которая была светлой, темной, а то, что было тем- ным — светлым. Конечно, все зависит от степени. Если область изображения была лишь чуть-чуть темноватой, она преобразуется в слегка светлую область и т. д. Негативное изображение создается путем вычитания значения элемента изображения из максимально возможного значения, равного 63. Соответственно белые элементы изображения преобразуются в черные. Эта передаточная функ- ция, представленная функцией «Negatelmage», также используется таблицами преобразований для своих операций. Для того чтобы сделать эту функцию бо- лее «подвижной», требуется пороговое значение, которое показывает программе,
Негативные изображения 331 Частичное вычитание изображения Порог = 50 В Рис. 9.4. Негативное изображение.
332 Глава, 9. Точечные процессы при каком значении элементов изображения начнется негативное изображение. Если порог равен нулю, то все значения элементов становятся отрицательны- ми, как было описано выше. Если порог положителен, его величина определя- ет значение элементов изображения, при которых начинается отрицательность изображения. Это используется, например для того, чтобы снять яркие аспек- ты изображения (превратить их в темные) таким образом, чтобы другие детали изображения стали явными. Негативное изображение полезно, если детально рассмотреть яркие части изображения. Человеческий глаз гораздо более восприимчив к деталям в темной области изображения, чем в светлой. На тестовом изображении (рис. 9.4) показан эффект негативного изображе- ния с помощью только ярких точек. Пороговое значение равно 50. Пороговые изображения Пороговые изображения — это метод превращения полутонового изображения в черное или белое изображение. Значения элементов изображения ниже опре- деленного значения порога, превращаются в черные, в то время как значения элементов изображения, большие или равные значению порога, превращаются в белые. Этот метод имеет очень широкое применение от искусства до обработ- ки изображения. Например, в искусстве полутоновые изображения с верным значением порога образуют так называемые эскизные наброски. Пороговое изо- бражение также используется как грубый метод получения печатной копии по- лутонового изображения на матричном принтере (гл. 7). В области машинного зрения изображения обычно подвергаются пороговому отсеканию до того, как будет проведено обнаружение краев. В этом случае по- роговое отсекание исключает из изображения ненужную информацию, которая может нарушить процесс обнаружения краев. Очень важно выбрать правиль- ное значение порога, чтобы убедиться, что в ходе порогового отсекания не будет теряться полезная информация. Как показано в листинге 9.1, функция «Thresholdlmage» также использу- ется в таблице преобразований. Вы на самом деле удивлены? В этой функции значения элементов в таблице преобразований, меньшие значения порогов, за- гружаются со значением 0 или черным цветом. Значения элементов таблицы преобразований, большие или равные значениям порога, устанавливаются бе- лыми или со значением 63. Функция «PtTransform» снова применяет таблицы преобразований к данным изображения. Как и все остальные функции обработки изображения, описанные в этой гла- ве, пороговое отсекание может применяться ко всему изображению или к части изображения, описанной «Со1», «Row», «Width» и «Height». Могут быть полу- чены неожиданные эффекты в процессе порогового отсекания только с частью изображения. Часть полутонового изображения может оказаться черной или бе- лой. Другие эффекты могут быть вызваны изменением черного или белого цве- тов порогового изображения, например, в оранжевый или лиловый. Получение порогового изображения из полутонового показано на рис. 9.5.
Увеличение контраста изображения 333 Рис. 9.5. Пороговое изображение. Увеличение контраста изображения Все мы имеем некоторые интуитивные представления о контрасте как о распре- делении света и тени в изображении. Изображения с низким контрастом могут характеризоваться либо как в основном светлые, либо в основном темные. Изо- бражения с высоким контрастом, с другой стороны, имеют как темные обла- сти, так и светлые области. Другими словами, изображения с высоким контрас- том используют весь диапазон яркости, соответствующий полутоновым изобра- жениям. Идеальным методом для проверки контраста изображения являются гисто- граммы. В совокупности с точечным процессом увеличения контрастности гисто- граммы могут в некоторых случаях использоваться для увеличения контраста изображения. С увеличением контраста изображения также увеличиваются ка- жущиеся детали изображения. Это и представляет собой явление человеческого глаза. Количество деталей изображения на самом деле не меняется. Изображения могут быть сгруппированы в одну из трех категорий контраста: изображения с низким контрастом, изображения с хорошим контрастом и изо- бражения с высоким контрастом. Как уже говорилось выше, все мы имеем инту-
334 Глава 9. Точечные процессы итивное представление о контрасте изображения и если нам даны изображения, мы тут же можем распределить их по трем категориям. Какими характеристи- ками обладают эти категории контраста, которые позволяют нам отличить эти изображения, и что отображают гистограммы каждой из этих категорий? Мы уже установили, какие качества определяют изображения с низким кон- трастом. Они состоят из тонов ограниченного диапазона (оттенки серого) и обыч- но либо слишком ярки, либо слишком темны (в некотором смысле похожи на переэкспонированные или недоэкспонированные фотоснимки). На гистограммах это условие регистрируется, когда все образцы изображения слегка группиру- ются друг с другом и занимают только малую часть возможных значений эле- ментов изображения образца. Если эта группировка значений элементов изо- бражения направлена в левую сторону гистограммы (по направлению к низким значениям образца), изображение будет черным. Если эта группировка напра- влена к большим значениям элементов изображения, изображение будет ярким. Обратим внимание на то, что возможно изображение низкого контраста, когда оно не будет ни слишком темным, ни слишком светлым. Это возможно, когда все образцы изображения группируются в центре гистограммы с несколькими зна- чениями элементов вне центра группировки. Изображения с низким контрастом имеют небольшой динамический диапазон. В изображении содержатся только некоторые возможные значения элементов изображения (значения яркости). В качестве примеров изображений, которые попадают в различные категории кон- траста, смотрите изображения на рис. 9.6. В изображениях с хорошим контрастом наблюдается широкий диапазон се- рых оттенков. На изображение с хорошим контрастом приятнее смотреть. Оно имеет широкий динамический диапазон, используя большинство возможных зна- чений элементов изображения. Это приравнивается к широкому диапазону се- рых оттенков в исходном изображении. Гистограммы изображений с хорошим контрастом показывают относительно однородное распределение значений эле- ментов изображения без больших пиков или впадин. Как и изображение с хорошим контрастом, изображения с высоким контрас- том содержат широкий диапазон серых оттенков. Однако изображения с высо- ким контрастом имеют большие площади, на которых преобладает темный цвет, и большие площади, которые имеют светлую окраску. Картина с ярким небом и темным задним планом является примером изобра- жения с высоким контрастом. Результатом этого на гистограмме показан двух- вершинный график (имеются два пика: один — в низких значениях элементов изображения, другой — в области высоких значений элементов изображения). Изображения с высоким контрастом должны хорошо использовать серые оттен- ки между двумя пиками, но визуально это перебивается преобладающими тем- ными и светлыми областями. В изображении с низким контрастом иногда может визуально повышаться качество с помощью увеличения контраста. Увеличение контраста используют гистограммы для того, чтобы определить, где находится группировка значений элементов в изображении с низким контрастом. Обыч- но эта группировка на гистограмме окружена значениями элементов изображе- ния, которыми никогда не пользуются. Для увеличения контраста мы сканируем гистограмму с самого нижнего значения элемента изображения вверх (от 0 до 63) и от самого высокого верхнего значения элемента изображения вниз (от 63
Увеличение контраста изображения 335 Малоконтрастное Высококонтрастное изображение Рис. 9.6. Изучение контрастов.
336 Глава 9. Точечные процессы Исходное изображение а Изображение с гистограммой б Изображение с усиленным контрастом в Рис. 9.7. Увеличение контраста изображения. Изображение с усиленным контрастом и гистограммой г до 0). Это делается для того, чтобы найти первое значение элемента изображе- ния, которое превышает определенный порог. Значения элементов изображения ниже нижнего порога равны нулю. Элементы изображения над верхним поро- гом устанавливаются на максимальное значение элементов, равное 63. Значе- ния элементов между двумя порогами находятся в полном диапазоне элементов от 0 до 63. Результатом этого процесса является изображение, которое лучше использует шкалу яркости. В изображениях на рис. 9.7 показаны результаты увеличения контраста изображения. Если сравнить изображение (а) до и по- сле увеличения контраста (в), становится очевидной полезность полной шкалы яркости.
Псевдораскрашивание 337 Функция «StretchlmageContrast» из библиотеки функций точечного процесса обработки изображения (см. листинг 9.1) демонстрирует операцию увеличения контраста. Отметим, что гистограммы должны создаваться до того, как мож- но применять эту программу. Это можно сделать, вызвав «Genllistogram» или «Displayllist». Как и ожидалось, можно использовать таблицу преобразований для демонстрации преобразования увеличения контраста. Передаточная функ- ция загружается в таблицу преобразований и для увеличения контраста изо- бражения вызывается функция «PtTransform». К изображениям с хорошим и высоким контрастом могут применяться другие виды преобразований. Можно преобразовать изображения с хорошим контрастом в изображения с высоким контрастом, и наоборот, с помощью выбора значений в таблице преобразований. Не все преобразованные изображения привлекают интерес. Некоторые могут быть визуально усилены, в то время как другие могут быть испорчены. Как уже говорилось выше, на результат не всегда будет приятно смотреть. Псевдораскрашивание Псевдораскрашивание — метод, который заменяет значение серого элемента изо- бражения и значения цветов в изображении. Этот метод используется для опо- знания элементов изображения в определенном диапазоне для различных целей. Например, если известно, что определенные значения элементов изображения отображают определенные температуры, то в изображении нити накаливания электрической лампы псевдораскрашивание можно использовать для лучшего различения температур. Самая высокая температура должна быть передана с помощью красного цвета, более низкие температуры — с помощью зеленых от- тенков, а самые низкие — с помощью синих. Результирующее изображение будет содержать то же количество информации, но информация о температуре будет легче восприниматься. Для псевдораскрашивания, таким образом, требуется подробная информация об изображении и об оборудовании, используемом при получении изображения. Определение сущности изображения не входит в нашу задачу. Однако, если уста- новлена корреляция между значением элементов изображения и информацион- ным содержанием изображения, применение псевдораскрашивания должно дать хорошие результаты. Вследствие большого разброса требований к псевдораскрашиванию не суще- ствует алгоритма для демонстрации этой функции. Для подготовки любого нуж- ного вам алгоритма вы можете воспользоваться некоторыми основными напра- влениями и примером изображения. Первым шагом в процессе псевдораскрашивания является выбор количества цветов и значений цветов, которые вы хотите использовать. Помните, что в ре- жиме VGA 13Н (режим, в котором выполнена вся обработка изображения, опи- санная в этой книге) требуется использование всех 256 цветов. Отображение полутоновых изображений использует только 64 из 256 цветов. Это означает, что 192 цвета пригодны для псевдораскрашивания. Как вы помните, каждый цвет контролируется с помощью цветового регистра VGA. 192 регистра доступ- ны для нашего использования (числа от 64 до 255), так как первые 64 числа уже используются (от 0 до 63). Например, для псевдораскрашивания требуется три 22 3
338 Глава 9. Точечные процессы цвета. Для этих трех цветов мы используем цветовые регистры 64, 65 и 66. Для того чтобы присвоить цвет по желанию этим трем цветовым регистрам, необхо- димо вызвать функцию «SetAColorReg», содержащуюся в библиотеке функций VGA (рассмотренной в гл. 1). Эта функция присваивает красный, зеленый и си- ний цвета цветовым регистрам. Размер каждого цветового атрибута составляет 6 бит. Это означает, что ваше псевдораскрашивание может охватывать диапазон 256000 возможных цветов. Возвращаясь к примеру, предположим, что вам тре- буется яркий красный, яркий зеленый и яркий синий псевдоцвета. Три вызова функции «SetAColorReg» сделают цветовой регистр 64 яркокрасным, регистр 65 яркозеленым и регистр 66 яркосиним. SetAColorReg(64, 63, 0, 0); SetAColorReg(65, 0, 63, 0); SetAColorReg(66, 0, 0, 63); Смотрите функцию «DisplayHist» для аналогичного примера. Так как назначены цветовые регистры, то необходимо установить соответ- ствие между элементами изображения определенного значения и псевдораскра- шиванием. Это осуществляется с помощью таблицы преобразований. Предполо- жим, что необходимо назначить красное псевдораскрашивание значениям эле- ментов изображения от 60 до 63, зеленое — от 56 до 59 и синее — от 52 до 55. Все другие значения элементов изображения сохранят обычные значения. Часть программы, которая приведена ниже, установит соответствие и продемонстриру- ет преобразование данных изображения. BYTE PseudoColorLUT(MAXQUANTLEVELS); /♦ Вызов функции InitializeLUTO устанавливает прямое соответствие для таблиц преобразования. Изменены будут только значения, которые необходимо изменить, исходя из значений по умолчанию. ♦/ InitializeLUT(PseudoColorLUT); /♦ Значениям элементов изображения 60-63 определите цветовой регистр 64. ♦/ PseudoColorLUT[63] = 64; PseudoColorLUT[62] s 64; PseudoColor LUT [61] = 64; PseudoColorLUT[60] = 64; /♦ Значениям элементов изображения 56-59 определите цветовой регистр 65. ♦/ PseudoColorLUT[59] = 65; PseudoColorLUT[58] s 65; PseudoColorLUT[57] = 65; PseudoColorLUT[56] = 65; /♦ Значениям элементов изображения 52-55 определите цветовой регистр 66. ♦/
Псевдораскрашивание 339 PeeudoColorLUTtSS] - 66; PseudoColorLUT[54] » 66; PseudoColorLUT[53] » 66; PseudoColorLUT[52] » 66; /* В этой точке инициируется вся таблица преобразований полностьп. Далее применяйте преобразование к данный изображения. ♦/ PtTransf ош(....); Изображение марсианского пейзажа показано на рис. 9.8. Исходное изображение Диапазон значений точек Цвет 0-51 Нормальный 52 - 53 Синий 56 - 59 Зеленый 60 - 63 Красный Рис. 9.8. Псевдораскрашиваиие. 22*
340 Глава 9. Точечные процессы Заключение Этой главой мы завершаем обсуждение точечных процессов. В ней рассмотрены не только различные типы точечных процессов, но и показано, каким полезным методом обработки изображения являются гистограммы, и каким эффектив- ным методом применения точечного процесса к данным изображения является таблица преобразований. Функции, описанные в библиотеке функций точечного процесса, полностью использованы в таблицах преобразований. После изучения точечных процессов мы может приступить к рассмотрению пространственных процессов. В последующих главах мы увидим, как точечные процессы в совокупности с другими алгоритмами обработки изображения уве- личивают наши возможности в исследовании и преобразовании изображений.
Глава 10 Пространственные процессы В главе рассмотрены следующие вопросы: ♦ Пространственные процессы и их использование. ♦ Свертка, пространственная фильтрация и определение края. Введение Пространственные процессы в некоторых работах называются групповыми про- цессами. Они используют группы элементов изображения для извлечения ин- формации об изображении. Этим пространственные процессы отличаются от то- чечных, которые используют информацию только одного элемента для осуще- ствления точечного процесса. Группа элементов изображения, используемых в пространственных процессах, называется областью примыкания. Область при- мыкания — двумерная матрица значений элементов изображения, каждое из- мерение которого имеет нечетное число элементов. Преобразуемые точки (точ- ки, старое значение которых заменено на новое как результат алгоритмиче- ских вычислений) расположены в центре области примыкания. Рассмотрение набора точек в непосредственной близости от преобразуемой точки дает инфор- мацию о распределении яркости (в двух направлениях), которая использует- ся большинством пространственных процессов. Другим, более правильным тер- мином для информации о распределении яркости является пространственная частота. Пространственная частота определяется как скорость изменения яркости элементов изображения или интенсивности, деленная на расстояние, на котором происходит это изменение. Пространственная частота имеет компоненты как го- ризонтального, так и вертикального направлений в изображении. Изображение с высокой пространственной частотой содержит резкие близкорасположенные из- менения в значениях соседних элементов. Изображение черно-белой шахматной доски является примером изображения с высокой пространственной частотой. Чем меньше квадраты этой доски, тем больше будет значение частоты. Изобра- жения с низкой пространственной частотой содержат большие области постоян- ных или медленно изменяющихся значений элементов изображения. Изображе- ния облаков обычно содержат низкую пространственную частоту. Информация о пространственной частоте позволяет использовать простран- ственные процессы в качестве фильтров путем удаления или усиления ком- понентов определенной частоты, обнаруженных в изображении. Таким обра- зом, многие пространственные процессы попадают в категорию пространствен- ных фильтров. Подобно электрическим фильтрам, пространственные филь- тры описываются математическими формулами. Ниже рассмотрены примеры
342 Глава 10. Пространственные процессы того, как используются пространственные фильтры без подробного описания используемого при этом математического обеспечения. Автор предлагает по всем вопросам математического обеспечения обращаться к книгам по цифро- вой обработке сигналов (некоторые из них приведены в разделе «Литерату- ра»). Необходимо отметить, что сложные математические выкладки алгорит- мов, используемые в этой главе, имеют в значительной степени интуитивное объяснение. Пространственная фильтрация находит широкое применение в обработке изо- бражений. Например, ее можно использовать для выделения некоторых призна- ков изображения (усиление границ и их определение), для увеличения резко- сти, сглаживания, размытия изображения и снятия случайных помех в изобра- жении. Эти аспекты пространственной фильтрации будут продемонстрированы ниже для того, чтобы показать эффект различных пространственных процессов в действии. В главе приведены три алгоритма пространственных процессов: свертка, усредненное фильтрование (медианная фильтрация) и определение краев по Со- белю. Алгоритм усредненного фильтрования имеет лишь одно применение, а ал- горитм свертки, более общий по природе, имеет много применений. Работа ка- ждого из этих алгоритмов похожа на работу алгоритмов, используемых в точеч- ных процессах. 1. Единичный переход осуществляется через входное изображение элемент за элементом. 2. Каждый элемент во входном изображении преобразуется в новый элемент. 3. Новое значение элемента помещено в выходном буфере изображения в той же ячейке, которая была взята из входного буфера изображения. Различие между точечным и пространственным процессами отражено в п. 2. При точечном процессе используется только значение (и иногда ячейка) входных элементов при создании выходного элемента. Из приведенного обсуждения становится очевидно, что в пространствен- ном процессе в преобразование вовлечено больше элементов, чем в точечном процессе. При использовании окрестности трансформируемого элемента раз- мером 3 х 3 с общим числом элементов 9 следует ожидать, что время об- работки примерно в 9 раз больше, чем в точечном процессе. В действитель- ности, как мы ниже увидим, время обработки гораздо больше. В результате пространственные процессы длятся достаточно долго и время сильно возра- стает с увеличением размера области примыкания преобразуемого элемента. Пространственные процессы, включающие цифры с плавающей запятой, мо- гут занимать еще больше времени. Например, выполнение процесса свертки с низким коэффициентом фильтрации на компьютере IBM PS/2 модель 70 (с ЦП 80386) с тактовой частотой 20 мГц может занимать до 3 мин. Точечные процессы, выполняемые в этом же компьютере, требуют нескольких секунд. Математический сопроцессор в вашем компьютере может существенно умень- шить время обработки благодаря вычислению алгоритмов пространственных процессов. Программа для всех пространственных процессов, описанных в главе, приве- дена в листинге 10.1 и содержит файлы arprocess.h и arprocess.c.
Введение 343 Листинг 10.1. Библиотека функций пространственного процесса Ниже приводится содержимое файла arprocess.h: /****************************************/ /♦ Header файл ♦/ /♦ Функции пространственной обработки ♦/ /♦ разработан в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /* Версия: 1.0 */ /♦ Последние изменения внесены 11/14/89 ♦/ /фффффффффффффффффффффффффффффффффффффффф/ /♦ Прототипы функций пространственной обработки изображений ♦/ CompletionCode Convolution (BYTE huge ♦InImage, unsigned Col,unsigned Ron, unsigned Width,unsigned Height, short ♦Kernel,unsigned KemelCols, unsigned KemelRovs,unsigned Scale, unsigned Absolute, BYTE huge ♦ ♦OutImageBufPtr); CompletionCode RealConvolut ion (BYTE huge ♦InImage, unsigned Col,unsigned Ron, unsigned Width,unsigned Height, short ♦Kernel,unsigned KemelCols, unsigned К erne IRovs, unsigned Scale, unsigned Absolute, BYTE huge ♦ ♦OutImageBufPtr); CompletionCode MedianFilter(BYTE huge ♦InImage, unsigned Col,unsigned Ron, unsigned Width,unsigned Height, unsigned NeighoborhoodCols, unsigned NeighoborhoodRovs, BYTE huge ♦ ♦Out ImageBufPtr) ; CompletionCode SobelEdgeDet(BYTE huge ♦InImage, unsigned Col,unsigned Ron, unsigned Width,unsigned Height, unsigned Threshold,unsigned Overlay, BYTE huge ♦ ♦OutImageBufPtr); Далее следует содержимое файла arprocess.c:
344 Глава 10. Пространственные процессы Листинг 10.1. (Продолжение) /****************************************/ /♦ Функции пространственной обработки ♦/ /♦ разработаны в Turbo С 2.0 ♦/ /♦ Крэйтон А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 11/07/89 ♦/ /****************************************/ finclude <stdio.h> #include <stdlib.h> finclude <conio.h> #include <dos.h> ftinclude <alloc.h> #include <process.h> #include <math.h> #include <graphics.h> #include "misc.h" #include "pcx.h" finclude "vga.h" #include "imagesup.h" finclude "arprocess.h" /♦ Целочисленная функция свертки ♦/ CompletionCode Convolution(BYTE huge ♦InImage, unsigned Col,unsigned Row, unsigned Width,unsigned Height, short ♦Kernel,unsigned KemelCols, unsigned KemelRows,unsigned Scale, unsigned Absolute, BYTE huge ♦ ♦OutImageBufPtr) { register unsigned ColExtent,RowExtent; register unsigned ImageCol,ImageRow,KemCol,KemRow; unsigned ColOffset.RowOffset,TempCol,TempRow; BYTE huge ♦ ♦OutputImageBuffer; long Sum; short ♦KernelPtr; if (ParameterCheckOK (Col,Row,Col+Width,Row+Height, "Convolution") ) { /♦ Изображение должно лежать в тех же пределах, что и ядро ♦/ if (Width >= KemelCols ftft Height >= KemelRows) { /♦ Запрашиваем буфер памяти для выходного изображения ♦/ Output ImageBuffer = (BYTE huge ♦) farcalloc(RASTERSIZE,(unisgned long)sizeof(BYTE)); if (Output ImageBuf fer «ж NULL) { restorecrtmode();
Введение 345 printf ("Error Not enough memory for convolution output buffer\n"); return (ENoMemory); /* Сохраняем адрес буфера выходного изображения */ ♦OutlmageBufPtr = Output ImageBuffer; /* Очищаем выходной буфер белым цветом, чтобы показать границу области, не затрагиваемой сверткой. Это также создает полезный белый кант в выходном изображении. */ Clear ImageArea(OutputImageBuffer, MINCOLNUM, MINROWNUM, MAXCOLS,MAXROWS,WHITE); ColOffset = KemelCols/2; RowOffset = KemelRows/2; /♦ Сдвигаем для учета краевых эффектов ♦/ Col +а ColOffset; Row +а RowOffset; Width -» (KemelCols - 1); Height -= (KemelRows - 1); /* Вычисляем новый диапазон обработки элементов изображения */ ColExtent а Col + Width; RowExtent a Row + Height; for(ImageRow a Row; Image < RowExtent; ImageRow++) TempRow = ImageRow - RowOffset; for(ImageCol = Col; Image < ColExtent; ImageCol++) TempCol = ImageCol - ColOffset; Sum a OL; KernelPtr a Kernel; for(KemCol a 0; KemCol < Kernel Cols; KernCol++) for(KemRow a 0; KemRow < KemelRows; KemRow++) Sum +a (GetPixelFromlmage(InImage,TempCol+KemCol, TempRow+KemRow) ♦ (*KernelPtr++)) ; /* Если требуется абсолютное значение */ if(Absolute) Sum a labs (Sum) ; /* Суммирование выполнено. Масштабируем сумму */ Sum »а (long) Scale; Sum = (Sum < MINSAMPLEVAL) ? MINSAMPLEVAL : Sum; Sum = (Sum > MAXSAMPLEVAL) ? MAXSAMPLEVAL : Sum; PutPixellnlmage (OutputImageBuffer, ImageCol, ImageRow, (BYTE) Sum) ; }
346 Глава 10. Пространственные процессы Листинг 10.1. (Продолжение) else гeturn(EKernelSize); return(NoError); /♦ Вещественная функция свертки. Эта функция используется, когда ядро представляется вещественными числами вместо целых. Из-за включения операций над вещественными числами эта функция работает существенно медленнее, чем целочисленная версия, приведенная выше. ♦/ CompletionCode RealConvolut ion (BYTE huge ♦InImage, unsigned Col,unsigned Rom, unsigned Width,unsigned Height, short «Kernel,unsigned KemelCols, unsigned KemelRovs,unsigned Scale, unsigned Absolute, BYTE huge ♦ «OutImageBufPtr) { register unsigned ColExtent,RovExtent; register unsigned ImageCol, ImageRov, KemCol, KemRow; unsigned ColOffset,RowOffset,TempCol,TempRow; BYTE huge ♦ «OutputImageBufter; double Sum; double «KemelPtr; if (ParameterCheckOK(Col ,Roh , Col+Width,Row+Height, “Convolution”) ) { /♦ Изображение должно лежать в тех же пределах, что и ядро*/ if (Width >= KemelCols ftft Height >s KemelRows) /♦ Запрашиваем буфер памяти для выходного изображения ♦/ Output ImageBuffer s (BYTE huge ♦) f arcalloc (RASTERSIZE, (unisgned long) sizeof (BYTE) ) ; if (Output ImageBuf fer s= NULL) { restorecrtmodeO; printf C’Error Not enough memory for convolution output buffer\n”); return (ENoMemory); 1 /♦ Сохраняем адрес буфера выходного изображения ♦/ ♦Out ImageBuf Pt г a Output ImageBuf fer; /♦ Очищаем выходной буфер белым цветом, чтобы показать границу области, не затрагиваемой сверткой. Это также создает полезный белый кант в выходном изображении. ♦/
Введение 347 ClearImageArea(Output ImageBuffer,MINCOLNUM,MINROWNUM , MAXCOLS,MAXROWS, WHITE); ColOffset = KemelCols/2; RowOffset = KemelRows/2; /♦ Сдвигаем для учета краевых эффектов ♦/ Col += ColOffset; Row +я RowOffset; Width -= (KemelCols - 1); Height -я (KemelRows - 1); /♦ Вычисляем новый диапазон обработки элементов изображения ♦/ ColExtent я Col + Width; RowExtent = Row + Height; for(ImageRow я Row; Image < RowExtent; ImageRow++) { TempRow = ImageRow - RowOffset; for(ImageCol » Col; Image < ColExtent; ImageCol++) TempCol = ImageCol - ColOffset; Sum =0.0; KemelPtr = Kernel; for(KemCol = 0; KemCol < KemelCols; KemCol++) for(KemRow = 0; KemRow < KemelRows; KemRow++) Sum += (GetPixelFromlmage (InImage 9 TempCol+KemCol , TempRow+KemRow) ♦ (*KemelPtr++)); /♦ Если требуется абсолютное значение ♦/ if(Absolute) Sum = fabs(Sum); /♦ Суммирование выполнено. Масштабируем сумму ♦/ Sum /= (double) (l«Scale); Sum = (Sum < MINSAMPLEVAL) ? MINSAMPLEVAL : Sum; Sum = (Sum > MAXSAMPLEVAL) ? MAXSAMPLEVAL : Sum; PutPixellnlmage (OutputImageBuf f er , ImageCol 9 ImageRow 9 (BYTE) Sum); > > > else retum(EKemelSize); > return (NoError); > /♦ Сравнение байтов для использования с библиотечной функцией быстрой сортировки, вызываемой в функции усредняющего фильтра. ♦/ int ByteCompare(BYTE *Entryl,BYTE *Entry2)
348 Глава 10. Пространственные процессы Листинг 10.1. (Продолжение) if(*Entryl < ♦Entry2) return (-1); else if(*Entryl > *Entry2) return (1); else return (0); CompletionCode MedianFilter(BYTE huge ♦InImage, unsigned Col,unsigned Row, unsigned Width,unsigned Height, unsigned NeighoborhoodCols, unsigned NeighoborhoodRovs, BYTE huge ♦ ♦OutImageBufPtr) register unsigned ColExtent,RowExtent; register unsigned ImageCol,ImageRow,NeighborCol,NeighborRov; unsigned ColOffset,RowOffset,TempCol,TempRov,Pixelindex; unsigned TotalPixels,Medianindex; BYTE huge ♦ ♦OutputImageBuffer; BYTE ♦PixelValues; if(ParameterCheckOK(Col,Row,Col+Width,Row+Height,"MedianFilter")) { /♦ Изображение должно включать область примыкания ♦/ if(Width >в NeighborhoodCols && Height >= NeighborhoodRovs) /♦ запрашиваем буфер памяти для выходного изображения ♦/ OutputImageBuffer в (BYTE huge ♦) fareallос(RASTERSIZE,(unisgned long)sizeof(BYTE)); if (Output ImageBuf fer BB NULL) { restorecrtmode(); printf("Error Not enough memory for convolution output bufferin’’); return (ENoMemory); /♦ сохраняем адрес буфера выходного изображения ♦/ ♦OutImageBufPtr B OutputImageBuffer; /♦ Очищаем выходной буфер белым цветом, чтобы показать границу области, не затрагиваемой усредняющим фильтром. Это также создает полезный белый кант в выходном изображении. ♦/ Clear ImageArea (Output ImageBuf fer ,MINCOLNUM ,MINROWNUM, MAXCOLS,MAXROWS,WHITE); /♦ Вычисляем границу так, чтобы элемент изображения находился в середине ♦/ ColOffset = NeighborhoodCols/2; RowOffset = NeighborhoodRows/2;
Введение 349 /♦ Сдвигаем для учета краевых эффектов */ Col +я ColOffset; Row += RowOffset; Width (NeighborhoodCols - 1); Height ~ж (NeighborhoodRows ~ 1); /♦ Вычисляем новый диапазон обработки элементов изображения ♦/ ColExtent = Col + Width; RowExtent = Row + Height; TotalPixels = (NeighborhoodCols*NeighborhoodRows); Medianindex ® (NeighborhoodCols*NeighborhoodRows)/2; /* запрашиваем память для буфера элемента изображения */ PixelValues=(BYTE *)calloc(TotalPixels,(unisgned long)sizeof(BYTE)); if(PixelValues == NULL) { restorecrtmodeO; printf(“Error Not enough nenory for convolution output buffer\n“); return (ENoMenory); for(InageRow « Row; Inage < RowExtent; InageRow++) { TenpRow « InageRow ~ RowOffset; for(InageCol « Col; Inage < ColExtent; InageCol++) { TenpCol » InageCol ~ ColOffset; Pixelindex » 0; f or (NeighborCol»0; NeighborCoKNeighborhoodCols; NeighborCol++) f or (Neighbor Ro w=0 ;NeighborRo w<Neighborho о dRows; NeighborRow++) PixelValues[Pixellndex++]я GetPixelFromlmage(InInage, TenpCol+NeighborCol,TenpRow+NeighborRow); /♦ Быстрая сортировка значений яркости в возрастающем порядке и выделение усредненного или среднего значения как значения данного элемента изображения. ♦/ qsort(PixelValues,TotalPixels,sizeof(BYTE),ByteConpare); PutPixellnlnage (Output ImageBuf f er, InageCol, InageRow, PixelValues [Medianindex] ); else return (EKemelSize); free(PixelValues); /* освобождаем буфер элемента изображения */ return(NoError); }
350 Глава 10. Пространственные процессы Листинг 10.1. (Продолжение) /* Функция определения краев по Собела. */ CompletionCode SobelEdgeDet (BYTE huge ♦ Ini mage, unsigned Col,unsigned Row, unsigned Width,unsigned Height, unsigned Threshold,unsigned Overlay, BYTE huge * *OutImageBufPtr) { register unsigned ColExtent,RowExtent; register unsigned ImageCol,ImageRow; unsigned PtA,PtB,PtC,PtD,PtE,PtF,PtG,PtH,PtI; unsigned LineAEIAveAbove, LineAEIAveBelow, LineAEIMaxDif; unsigned LineBEHAveAbove, LineBEHAveBelow, LineBEHMaxDif; unsigned LineCEGAveAbove,LineCEGAveBelow,LineCEGMaxDif; unsigned LineDEFAveAbove, LineDEFAveBelow, LineDEFMaxDif; unsigned MaxDif; BYTE huge * «OutputImageBuffer; if (ParameterCheckOK(Col ,Row,Col+Width,Row+Height,"Sobel Edge Detector")) /* Запрашиваем память для буфера выходного изображения */ OutputImageBuffer я (BYTE huge *) farcalloc(RASTERSIZE,(unisgned long)sizeof(BYTE)); if (Output ImageBuffer == NULL) { restorecrtmodeO; printf ("Error Not enough memory for convolution output buffer\n") ; return (ENoMemory); /* Сохраняем адрес буфера выходного изображения */ ♦OutlmageBufPtr = OutputImageBuffer; /♦ Очищаем выходной буфер ♦/ Clear ImageArea (Output ImageBuf f er, MINCOLNUM, MINROWNUM, MAXCOLS,MAXROWS,WHITE); /* Сдвиг для учета краевых эффектов от 3x3 ближайших элементов */ Col += 1; Row +я 1; Width -я 2; Height -я 2; /♦ Вычисляем новый диапазон обработки элементов изображения */ ColExtent я Col + Width; RowExtent я Row + Height; for(ImageRow s Row; Image < RowExtent; ImageRow++) { for(ImageCol я Col; Image < ColExtent; ImageCol++)
Введение 351 { /♦ Выбираем значение каждого из 3x3 соседей элемента изображения ♦/ PtA = GetPixelFronlnage (InInage, InageCol-1, InageRow-1) ; PtB » GetPixelFronlnage(InInage,InageCol ,InageRow-1); PtC « GetPixelFronlnage (InInage, InageCol+1, InageRow-1) ; PtD ж GetPixelFronlnage(InInage,InageCol-1,InageRow ); PtE « GetPixelFronlnage (InInage, InageCol , InageRow ) ; PtF ж GetPixelFronlnage (InInage, InageCol*1, InageRow ) ; PtG = GetPixelFronlnage(InInage,InageCol-1,InageRow+1) ; PtH - GetPixelFronlnage(InInage,InageCol , ImageRow+1); PtI = GetPixelFronlnage(InInage,InageCol+1,InageRow+1) ; /♦ Вычисляем среднее из выше- и нижележащих линий. Берем абсолютное значение разности. */ LineAEIAveBelow = (PtD+PtG+PtH)/3; LineAEIAveAbove = (PtB+PtC+PtF)/3; LineAEIMaxDif ® abs(LineAEIAveBelow-LineAEIAveAbove); LineBEHAveBelow « (PtA+PtD+PtG)/3; LineBEHAveAbove « (PtC+PtF+PtI)/3; LineBEHMaxDif = abs(LineBEHAveBelow-LineBEHAveAbove); LineCEGAveBelow « (PtF+PtH+PtI)/3; LineCEGAveAbove = (PtA+PtB+PtD)/3; LineCEGMaxDif = abs(LineCEGAveBelow-LineCEGAveAbove); LineDEFAveBelow « (PtG+PtH+PtI)/3; LineDEFAveAbove « (PtA+PtB+PtC)/3; LineDEFMaxDif = abs(LineDEFAveBelow-LineDEFAveAbove); /* Ищем максимальное значение абсолютной разности из четырех возможных. ♦/ MaxDif = MAX(LineAEIMaxDif,LineBEHMaxDif) ; MaxDif = MAX(LineCEGMaxDif,MaxDif); MaxDif « MAX(LineDEFMaxDif,MaxDif); /♦ Если максимальная разность больше порога, устанавливаем значение интересующего элемента (центрального) в белый цвет. Если меньше, то копируем входное изображение в выходное. Это копирование контроллируется параметром Overlay. ♦/ if (MaxDif >= Threshold) PutPixelInInage (OutputInageBuf f er, InageCol, InageRow, WHITE) ; else if(Overlay) PutPixelInInage (OutputlnageBuf f er, InageCol, InageRow, PtE) ; 1 1 return (NoError); 1
352 Глава 10. Пространственные процессы Листинг 10.2 содержит исходный текст программы, используемой для созда- ния всех изображений в данной главе, и файлы artest.c и artest.prj. Программа спроектирована для использования библиотеки функций пространственного про- цесса, должна содержать файл arprocess.h и должна быть собрана с arprocess.c. Используйте artest.c как модель для типичной программы применения. Прототипы функций и описание пространственных процессов, содержащихся в библиотеке функций пространственных процессов, приведены в табл. 10.1. Листинг 10.2. Пример программы пространственного процесса Ниже приводится текст файла artest.prj: graphics.lib vgagraph.obj egavga.obj pcx (misc.h pcx.h) vga (mics.h vga.h pcx.h) imagesup (mics.h vga.h psx.h imagesup.h) arprocess (mics.h vga.h psx.h imagesup.h) artest (mics.h vga.h psx.h imagesup.h ptprocess.h) Далее следует текст файла artest.c: /****************************************/ /* Демонстрационная программа */ /♦ пространственных методов обработки ♦/ /* изображения */ /♦ разработана в Turbo С 2.0 */ /* Крэйгом А. Линдли ♦/ /♦ */ /* Версия: 1.0 */ /* Последние изменения внесены 12/26/89 */ /****************************************/ tindude <stdio.h> /♦ tindude <stdlib.h> ♦/ tindude <conio.h> tindude <dos.h> tindude <alloc.h> tindude <process.h> tindude <graphics.h> tindude "misc.h" tindude "pcx.h" tindude "vga.h" tindude " imagesup. h" tindude "arprocess.h" static double LP1[] = { 0.11111111, 0.11111111, 0.11111111, 0.11111111, 0.11111111, 0.11111111, 0.11111111, 0.11111111, 0.11111111
Введение 353 static short HP1[] = { -1,-1,-1, “1, 9,-1, -1,-1,-1 >; static short LAP2[] = { -1,-1,-1, “1, 8,-1, -1,-1,-1 static short GN[]e { 1, 1, 1, 1,-2, 1, -i,-i,-i >; static short GE[]» { -1, 1, 1, -1,-2, 1, -1, 1, 1 >; static short BLUR[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; /* основная демонстрационная программа пространственной обработки изображений */ void main(void) { char *InFileNamel я "p2cl0ila"; char *InFileName2 я ”p2cl0i2a"; char *InFileName3 я "p2cl0i3a"; BYTE huge ♦TheInimage; BYTE huge *TheOutImage; unsigned GenPCXFiles = FALSE; /* управляет созданием выходного файла */ InitGraphics (); printf (“Area Process Demonstration program\n\n") ; printf ("Reading the Image PCX File into memory\n"); /* создание последовательности изображений на рис. 10.3 - Различные свертки */ if (ReadPCXFileToBuf (InFileNamel,ATheInImage) !я NoError) exit(1); DisplayImagelnBuf(Thelmage,INITVGALOADPALETTE,NOWAITFORKEY); if (RealConvo lut ion (The InImage, MINCOLNUM, MINROWNUM, MAXCOLS, MAXROWS, LP1,3,3,0,FALSE,ATheOutImage) яя NoError) 23-3
354 Глава 10. Пространственные процессы Листинг 10.2. (Продолжение) { DisplaylmagelnBuf(TheOutlmage.NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2clOilb",8,320,200,1,320); farfree((BYTE far ♦)TheOutImage); } DisplaylmagelnBuf(Thelnlmage.NOVGAINIT.NOWAITFORKEY); if (Convolution(TheInImage ,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, HP1,3,3,0,FALSE,ATheOutImage) “ NoError) { DisplaylmagelnBuf(TheOutlmage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) Writ ePCXFile("p2clOi1c",8,320,200,1,320); farfree((BYTE far *)TheOutImage); } DisplaylmagelnBuf(Thelnlmage.NOVGAINIT.NOWAITFORKEY); if(Convolution(TheInImage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, LAP2,3,3,0,FALSE,ATheOutlmage) NoError) { DisplaylmagelnBuf(TheOutlmage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2clOiId",8,320,200,1,320); farfree((BYTE far *)TheOutImage); } DisplaylmagelnBuf(Thelnlmage.NOVGAINIT,NOWAITFORKEY); if(Convolution(TheInImage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, GN,3,3,0,FALSE,ATheOutlmage) ““ NoError) < DisplaylmagelnBuf(TheOutlmage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2clOile",8,320,200,1,320); farfree((BYTE far *)TheOutImage); DisplaylmagelnBuf(Thelnlmage,NOVGAINIT.NOWAITFORKEY); if(Convolutiontthelnlmage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, GE,3,3,0,FALSE,NTheOutImage) “ NoError) { DisplaylmagelnBuf(TheOutlmage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2clOiIf",8,320,200,1,320); farfree((BYTE far *)The0utImage); DisplaylmagelnBuf(Thelnlmage,NOVGAINIT,NOWAITFORKEY); if(Convolution(TheInImage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, BLUR,5,5,4,FALSE,ftThe0utImage) ”= NoError) { DisplaylmagelnBuf(TheOutlmage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2clOi1g",8,320,200,1,320); farfree((BYTE far *)TheOutImage);
Введение 355 /* создание последовательности изображений на рис. 10.4 - Определение краев по Собел» */ if(ReadPCXFileToBuf(InFileNane2,ATheInInage) !ж NoError) exit(l); DisplayInageinBuf(Thelnage,NOVGAINIT.NOWAITFORKEY); if(SobelEdgeDet(Thelnlnage.MINCOLNUM.MINROWNUM.MAXCOLS.MAXROWS, 10,FALSE,ATheOutlnage) == NoError) { DisplayInageinBuf(TheOutlnage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2cl0i2b",8,320,200,1,320); farfree((BYTE far *)The0utInage); /* снова делаем обнаружение, но в этот раз с Overlay-TRUE */ DisplayInageinBuf(Thelnage,NOVGAINIT,NOWAITFORKEY); if(SobelEdgeDet(Thelnlnage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, 10,TRUE,ATheOutlnage) “ NoError) { DisplayInageinBuf(TheOutlnage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2c10i2c",8,320,200,1,320); farfree((BYTE far *)TheOutInage); /* создание последовательности изображений на рис. 10.6 - Медианная фильтрация */ if(ReadPCXFileToBuf(InFileNane3,ATheInInage) != NoError) exit(l); DisplaylnagelnBuf(Thelnage,NOVGAINIT,NOWAITFORKEY); if(MedianFilter(Thelnlnage,MINCOLNUM.MINROWNUM,MAXCOLS,MAXROWS, 3,3,ATheOutlnage) жж NoError) { DisplaylnagelnBuf(TheOutlnage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2c10i3b",8,320,200,1,320); CopyInage(TheOutlnage,Thelnlnage); farfree((BYTE far *)TheOutInage); /♦ применяем фильтрации дважды для усиления эффекта ♦/ DisplaylnagelnBuf(Thelnage,NOVGAINIT,NOWAITFORKEY); if(MedianFilter(Thelnlnage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, 3,3,ATheOutlnage) ” NoError) DisplaylnagelnBuf(TheOutlnage,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2cl0i3c",8,320,200,1,320); CopyInage(TheOutlnage,Thelnlnage); farfree((BYTE far ♦)TheOutlnage); 23»
356 Глава 10. Пространственные процессы Листинг 10.2. (Продолжение) farfree ((BYTE far ♦)TheInInage); restorecrtnodeO; closegraph(); Таблица 10.1. Функции пространственной обработки 1. Функция свертки целых чисел. Прототип CompletionCode Convolution(BYTE huge *lnlmage, unsigned Col, unsigned Row, unsigned Width, unsigned Height, short *Kernel, unsigned KemelCols, unsigned KemelRows, unsigned Scale, unsigned Absolute, BYTE huge *Out- ImageBufPtr); где «Inlmage» — указатель на вход буфера изображения, «Со1», «Row», «Width» и «Height» — параметры, описывающие часть входного изображе- ния, к которой применяется свертка. «Кете!» — указатель на ядро свертки, состоящее из беззнаковых целых чисел. «KemelCols» и «KemelRows» — размеры ядра свертки. Обычно эти значе- ния равны 3 и 3 для ядра свертки 3x3. «Scale» — степень числа 2, на которое делится сумма свертки для того, чтобы промасштабировать результаты в рабочий диапазон значений точек от 0 до 63. Значение 0 свидетельствует о том, что сумма свертки не пре- образуется. «Absolute» — флаг, который, будучи установлен в значение TRUE, уста- навливает сумму свертки в абсолютное значение. Будучи установленным в FALSE, отрицательные компоненты свертки будут равны 0. «OutlmageBufPtr» — указатель на адрес хранения указателя на буфер вы- ходного изображения. Действие Функция свертки начинается с проверки полученных параметров, чтобы гарантировать их правильность. Если обнаружены неправильные параме- тры, программа останавливается и выводится сообщение об ошибке. Затем буфер изображения размещается во входное изображение. Если обнаружи- вается недостаточность памяти, функция возвращает код «ENoMemory». При успешном размещении буфера изображения в дальней области памяти адрес буфера записывается в ячейке, на которую указывает «OutlmageBuf- Ptr». Таким образом, программа возвращает адрес буфера выходного изо- бражения. Размещенный буфер выходного изображения очищается до бе- лого цвета таким образом, что действия функции свертки будут визуально очевидны при отображении выходного изображения. Граничные площади, которые не подлежат обработке функции свертки, останутся белыми при выведении изображения на экран. (Из-за обрезания изображения при изда- нии книги эти белые края не всегда видны.) Параметры, которые определя- ют площадь изображения, подлежащую свертке, затем модифицируются,
Введение 357 чтобы скомпенсировать краевые эффекты ядра свертки. «Со1» и «Row» должны увеличиваться на половину размера ядра, a «Width.» и «Height» уменьшаются на единицу от размера ядра. После того как выполнены регу- лировки, выполняются вложенные циклы, которые проводят вычисление для каждой точки в указанной зоне изображения. Когда заполняется сум- ма каждого преобразованного интересующего нас элемента изображения, берется абсолютное значение и сумма может быть промасштабирована в зависимости от параметров функции свертки. Наконец, проверяются но- вые значения элементов изображения, для того чтобы убедиться, что они находятся в правильном диапазоне значений. Затем новые значения поме- щаются в выходное изображение на то же место, из которого они были взяты во входном изображении. 2. Функция сеертки действительных чисел. Прототип CompletionCode RealConvolution(BYTE huge *lnlmage, unsigned Col, unsigned Row, unsigned Width, unsigned Height, double *Kernel, unsigned KemelCols, unsigned KemelRows, unsigned Scale, unsigned Absolute, BYTE huge *Out- ImageBufPtr); где все параметры определены выше, кроме «Kernel», кото- рое является теперь указателем на ядро свертки, состоящим из двойных беззнаковых целых вместо беззнаковых целых чисел. Действие Действие этой такое же, как действие предыдущей функции, за исклю- чением того, что сумма и ядра используют реальные числа с плавающей запятой вместо целых чисел. 3. Функция определения краев по Собелю. Прототип CompletionCode SobelEdgeDet(BYTE huge *lnlmage, unsigned Col, unsigned Row, unsigned Width, unsigned Height, unsigned Threshold, unsigned Overlay, BYTE huge **OutImageBufPtr); где «Threshold» — величина, которая определяет значение перехода между белыми и черными значениями элементов изображения. Значения элемен- та, равного или превышающего этот порог, приводит к появлению белого элемента в выходном изображении. «Overlay» — флаг, который определяет, можно ли использовать значение исходного элемента в качестве выходного. Копирование будет проведено в том случае, когда абсолютная разность меньше определенного порога. Если абсолютная разность равна пороговому значению или больше его, выходной элемент изображения автоматически становится белым. Если «Overlay» FALSE, исходное изображение не копируется и соответственно элемент будет черным. Действие Начало выполнения этой функции такое же, как и для свертки, поэтому мы не будет его повторять здесь. Однако поточечный процесс существенно отличается, и мы его рассмотрим ниже. Эта функция (и ее описание) ис- пользует область примыкания 3x3. Любые изменения в размере области приведут к изменениям в программе функции.
358 Глава 10. Пространственные процессы Таблица 10.1. (Продолжение) В каждой ячейке имеется 9 значений, которые составляют область при- мыкания. Затем вычисляются средние значения интенсивности трех точек выше и ниже 4 возможных линий раздела (см. текст). Затем вычисляет- ся абсолютное значение разностей интенсивности. Максимальная разность из четырех линейных вычислений сравнивается с пороговым параметром. Если разность превышает значение порога, выходной элемент изображения устанавливается на белый цвет (присваивается значение, равное 63). Если разность меньше значения порога, либо выходной элемент изображения остается черным (цвет которого инициировал выходной буфер изображе- ния), либо исходный элемент изображения копируется в выходное изобра- жение. Копирование зависит от параметра «Overlay». Если все завершается нормально, эта функция возвращает значение «NoError». 4. Функция усредняющего фильтра. Прототип CompletionCode MedianFilter(BYTE huge *lnlmage, unsigned Col, unsigned Row, unsigned Width, unsigned Height, unsigned NeighborhoodCols, unsigned NeighborhoodRows, BYTE huge **OutImageBufPtr); где «InImage», «Col», «Row», «Width», «Height» и «OutlmageBufPtr» рассматривались выше. «NeighborhoodCols» и «NeighborhoodRows» определяют размер области примыкания элементов изображения для их использования во время про- цесса фильтрации. Обычно используют область примыкания 3x3. Действие Функция работает почти так же, как и свертка. Инициирование и регули- ровки краев выполняются так же, как и для функции свертки. Отличие этой функции заключается в том, что значения элементов в области примы- кания вместо суммирования быстро сортируются по восходящему порядку. Среднее значение элемента затем присваивается рассматриваемому элемен- ту и последний помещается в выходное изображение. Если все завершается нормально, эта функция возвращает значение «NoError». Свертка Как уже говорилось выше, свертка — это алгоритм очень общего назначения, который можно использовать в различных преобразованиях пространственных процессов. Несмотря на то что это звучит сложно, в действительности свертку легко понять и осуществить. В этой главе показаны примеры визуальных эф- фектов свертки. На рис. 10.1 показан процесс свертки. Самый лучший путь к пониманию свертки — это представить ее как процесс линейной комбинации. Каждый элемент в области примыкания (на рис. 10.1 она предполагается равной 3x3) умножается на ядро свертки аналогичной размерно- сти. Результирующая сумма при этом заменяет значение рассматриваемого эле- мента изображения. Каждый элемент ядра свертки — это весовой фактор (назы- ваемый также коэффициентом свертки). Размеры и структура весовых факто- ров, содержащихся в ядре свертки, определяют тип пространственного преобра- зования и применяются к данным изображения. Изменение весового фактора в
Свертка 359 Исходное изображение Окружение точки 3x3 Рассчитывается точка Р5 Вычисление взвешенной Ядро преобразования 3x3 суммы (К,- PJ + (К2* Р2) + (К3-р3) + (К4-Р.) + (KS*PS) + (К.*Р.) + (К,- р7) + (К,*Р,) + Новое значение Р5 Рис. 10.1. Свертка.
360 Глава 10. Пространственные процессы пределах ядра свертки влияет на величину и, возможно, на знак общей суммы и, таким образом, воздействует на значение рассматриваемого элемента изображе- ния. На рис. 10.2 показаны различные ядра свертки и передаточные функции, которые они представляют. Как видно из рисунка, большинство матриц ядер имеют размер 3 х 3 и нечетное число рядов и колонок. Этот формат коэффи- циентов свертки в пределах ядра используется в промышленности в качестве стандарта. Большой размер ядра повышает гибкость процесса свертки. Автор предста- вляет вам возможность самим поэкспериментировать с ядрами. К сожалению, некоторые детали простого расчета весовой суммы свертки усложняют его реализацию. Первая и самая основная трудность связана с кром- ками изображения. По мере того как мы перемещаем ядро свертки (и соответ- ственно рассматриваемый элемент изображения) по изображению элемент за элементом, у нас возникнут сложности потому, что на границе изображения рассматриваемый элемент не имеет соседей со всех сторон. Другими словами, ядро свертки выходит краем за границы изображения. Такое искажение про- исходит на верхней, правой, левой и на нижней границах изображения. Для устранения такого явления можно использовать несколько методов. Наиболее исчерпывающими решениями являются: 1) данные на краях изображения не учитываются или 2) данные изображения могут копироваться, для того чтобы «синтезировать» дополнительные данные границы. Первый метод используется в библиотеке функций пространственного процесса. Вторая трудность связана с динамичностью диапазона новых значений пре- образуемых элементов. Для большинства ядер новые значения преобразуемых элементов находятся в пределах действительных значений: от 0 до 63. К сожале- нию, ядро размытия дает значения, выпадающие из диапазона действительных значений элементов изображения. По этой причине в алгоритм свертки вводится масштабирование. Параметр «Scale», передающийся в функцию свертки, опреде- ляет, сколько раз вычисленная сумма весов будет делиться на 2, до того как она присваивается преобразуемым элементам изображения. Если параметр «Scale» равен нулю, весовая сумма не будет изменяться. При тщательном анализе становится понятным, почему ядро размытия дает такие большие значения, в то время как другие ядра, приведенные на рис. 10.2, так хорошо себя «ведут». Отметим, что во всех ядрах, за исключением ядра размытия, сумма всех весовых коэффициентов равна либо 0, либо 1. Сумма ко- эффициентов ядра размытия равна 25. Это означает, что расчет весовой суммы будет равен 25 значениям элементов изображения в области примыкания 5x5. Если, например, каждый из 25 элементов изображения имеет значение, равное половине максимального значения, или 32, то сумма будет равна 25 х 32, или 800. Для того чтобы установить эту сумму в соответствующий диапазон, приме- няется параметр «Scale», равный 4 или 5. Это уменьшит значение весовой суммы соответственно до 50 или 25. Окончательный выбор параметра «Scale» является отчасти субъективным и зависит от вида изображения. И последнее, что необходимо учесть в алгоритме свертки, это знак значе- ний элементов изображения. Если ядро свертки содержит отрицательные ве- совые коэффициенты, что характерно для большинства ядер, можно получить отрицательное значение интенсивности элементов изображения. Хотя значения «отрицательной интенсивности» интересны, мы не можем показать их. По этой
Свертка 361 Низкочастотные пространственные фильтры 1 1 1 1 1 1_ 1 1 1 9 9 9 Ю 10 Ю 16 8 16 1 1 1 1 1 ± 1 1 1 9 9 9 10 5 ю 8 4 8 1 1 1 1 1 1 1 ± 9 9 9 ю 10 ю 16 8 16 LP1 LP2 LP3 1-1 Высокочастотные пространственные фильтры —1 —1 —1 0 -1 0 1 -2 1 —1 9 -1 —1 5 —1 -2 5 -2 —1 —1 —1 0 —1 0 1 -2 1 НР1 НР2 НРЗ 1-1 Усиление края методом сдвига и разности Усиление края методом выравнивающего фильтра 0 0 0 0-10 -10 0 -1 0 1 —1 —1 —1 —1 —1 -1 1 0 0 1 0 0 1 0 —1 0 1 0 0 0 0 0 0 0 0 ООО 0 0 0 —1 0 1 11111 Вертикаль - ные края Горизонталь* ные края 1-о Вертикаль- ные и горизонтальн ные края —1 -1 0 0 1 1 1-0 Усиление края методом направленного градиента Ядро размытия 111 111 -1 1 1 -1 -1 1 1 1 1 1 1 1-2 1 -1-2 1 -1 -2 1 -1 -2 1 1 1 1 1 1 -1 -1 -1 -1 -1 1 -1 1 1 1 1 1 1 1 1 1 1 Север Северо-восток Восток Юго-восток 1 1 1 1 1 —1 —1 —1 1 —1 —1 1 1 -1 1 1 1 1 1 1 1 1 1-2 1 1-2-1 1 -2 -1 1 -2 1 £.25 111 111 1 1 -1 —1 —1 —1 Юг Юго-запад Запад Северо-запад 1 «0 Усиление края по Лапласу 0 1 0 -1 -1 -1 -1 -1 -1 1 -2 1 1 -4 1 -1 8 -1 -1 9 -1 -2 4 -2 0 1 0 -1 -1 -1 -1 -1 -1 1 -2 1 LAP1*3 LAP2 LAP32 LAP4 обычно 1 = 0 кроме LAP3 Рис. 10.2. Различные ядра свертки. Примечания 1. Все ядра получены от Baxes, за исключением трех, полученных от Dawson. 2. Эквивалентно добавлению исходного изображения к свернутому с ядром LAP2. 3. Доказательство можно прочитать у Gonzalez и Wintz.
362 Глава 10, Пространственные процессы причине реализация программы свертки устанавливает отрицательное значение интенсивности элементов изображения равными нулю. Для того чтобы рассмо- треть отрицательные значения интенсивности, используются другие методы. На- пример, можно использовать абсолютное значение интенсивности вместо присва- ивания значения, равного нулю. Другой путь состоит в масштабировании всех значений элементов таким образом, чтобы отрицательные значения стали рав- ными нулю. Любую из приведенных здесь обработок можно легко добавить в программу свертки. Программа свертки, описанная выше, состоит из двух частей: целочисленной свертки, которая использует ядра свертки с целыми коэффициентами, и про- грамма свертки с действительными числами, которая используется в ядрах с действительными коэффициентами. По этой причине представлено два алгорит- ма. Как и ожидалось, реализация целочисленного метода выполняется значи- тельно скорее, чем в алгоритме с плавающей запятой. Целочисленная свертка используется для всех пространственных процессов, за исключением фильтров с низкой передачей. Для пространственных фильтров требуется ядро с дробными коэффициентами, и поэтому в расчетах свертки они используют действительные числа. Обе функции приведены в листинге 10.1. Низкочастотные пространственные фильтры Низкочастотные пространственные фильтры оставляют низкочастотные компо- ненты изображения нетронутыми и ослабляют высокочастотные компоненты. Такие фильтры используются для понижения визуального шума, содержащего- ся в изображении, а также для удаления высокочастотных компонент из изобра- жения с тем, чтобы можно было тщательнее исследовать содержание низкоча- стотных компонент. По мере того как уменьшается содержание высокочастотных компонент, могут быть идентифицированы более слабые изменения низкой ча- стоты. Частота отсечки низкочастотного фильтра определяется размером и ко- эффициентами ядра. На рис. 10.2 показаны 3 различных низкочастотных ядра. Отметим, что сумма значений ядра для всех низкочастотных фильтров равна 1. Этот факт важен для понимания работы низкочастотных фильтров. Рассмотрим часть изображения, не содержащую высокочастотных компо- нент. Это означает, что все значения элементов изображения постоянные или они изменяются очень медленно. По мере того как низкочастотное ядро проходит через область изображения, новое значение преобразуемых элементов изображе- ния (элементы, находящиеся вокруг ядра) вычисляется как сумма коэффици- ентов ядра, умноженных на значения элементов изображения в области примы- кания. Если все значения элементов изображения в области примыкания оди- наковы (постоянны), новые значения элементов изображения будут такими же, как и старые. По этой причине сумма коэффициентов выбрана равной 1. Сохра- няется содержание низкочастотных компонент. По мере того как ядро движется по области изображения с содержанием высокочастотных компонент, любые бы- стрые изменения интенсивности усредняются с оставшимися элементами изобра- жения в области примыкания, тем самым понижая содержание высокочастот- ных компонент. Визуальным результатом низкочастотной фильтрации является слабая нерезкость изображения. Она возникает потому, что любые увеличения
Свертка. 363 значений элементов изображения усредняются с их окружением, что ослабля- ет содержание высокочастотных компонент. На рис. 10.3,а показано исходное изображение, а на 10.3, — изображение после низкочастотной фильтрации. Эти преобразования могут длиться очень много времени из-за использования чисел с плавающей запятой при расчете свертки для низкочастотных фильтров. Для того чтобы произвести изображение на компьютере IBM PS/2 модели 70, потребовалось около 3 мин. Наличие цифрового сопроцессора значительно уско- ряет эти расчеты. Низкочастотная фильтрация используется для улучшения резкости изобра- жения. Если отфильтрованное изображение с помощью низкочастотного филь- тра вычитается из исходного изображения, то в результате будет относитель- ное увеличение содержания высокочастотных компонент без увеличения шума в изображении. Субъективно результирующее изображение кажется резче, чем исходное. Это может быть использовано для освещения областей изображения, которые затемнены туманом или облаками. Используя эту методику, можно сде- лать хорошим изображение Лос-Анджелеса в туманный день. Высокочастотные пространственные фильтры Высокочастотные фильтры выделяют высокочастотные компоненты изображе- ния, оставляя содержание низкочастотных компонент нетронутым. Содержание низкочастотных компонент увеличивается по отношению к содержанию высо- кочастотных компонент. Такая фильтрация используется в тех случаях, когда необходимо исследовать объекты с высокой пространственной частотой. Области изображения с высокой частотой будут хорошо освещены (станут ярче), а части с низкой частотой станут черными. Иногда резкость изображения увеличивается после высокочастотной фильтрации за счет выделения шума изображения. При использовании высокочастотной фильтрации возможно усиление края изобра- жения. На рис. 10.2 показаны 3 ядра высокочастотных фильтров. На рис. 10.3 показан результат применения высокочастотной фильтрации. Большое значение коэффициента центра ядра определяет действие высоко- частотного фильтра. По мере того как центр с большим коэффициентом переме- щается по области изображения с высокой пространственной частотой (что озна- чает большие изменения интенсивности элементов изображения), новое значение рассматриваемых элементов изображения многократно увеличивается. Меньшие отрицательные значения коэффициента ядра, сгруппированные вокруг центра, уменьшают эффект большого весового фактора. В конечном итоге, большие из- менения интенсивности элементов изображения усиливаются, а области посто- янной интенсивности элементов изображения останутся неизменными. Другими словами, области постоянной интенсивности элементов изображения (области низких пространственных частот) не подвергаются этим преобразованиям. Усиление края Другим пространственным процессом, который можно продемонстрировать, ис- пользуя свертку, является усиление края. Усиление края используется как пред- варительный шаг в процессе извлечения признаков изображения (обычно еле-
364 Глава 10. Пространственные процессы Исходное изображение В
Свертка 365 Рис. 10.3. (Продолжение) Усиление кромки градиентом (восток) дует за пороговым точечным процессом). Алгоритмы усиления края сжимают изображение до краев, оно уменьшается и во многих случаях полностью уничто- жается. По этой причине обработанное изображение может сильно отличаться от исходного изображения. Яркость края после усиления пропорциональна из- менению яркости, окружающей край в исходном изображении. Хотя усиление края в основном используется в машинном зрении, оно, ко- нечно, имеет и другие применения. Например, информация о крае, полученная в процессе его усиления, может быть использована в исходном изображении для усиления его резкости. Усиление края можно использовать как художествен- ный метод для изготовления оригинальных изображений, которые могут затем ретушироваться в программах рисования для создания высокохудожественного изображения.
366 Глава 10, Пространственные процессы Ниже мы рассмотрим 3 различных метода усиления края и один метод опре- деления кромки. В этих методах используется анализ интенсивности элементов изображения, содержащихся в области примыкания рассматриваемого элемента изображения. Распределение яркости в области примыкания используется для нахождения и штрихования краев, содержащихся в изображении. По определе- нию край представляет собой область с большим значением интенсивности. Необ- ходимо отметить, что все алгоритмы усиления края, которые используют сверт- ку, являются линейными. Поэтому они образуют сумму произведений первой степени. Алгоритм определения краев по Собелю использует первые производ- ные для определения краев. По этому нелинейному методу достаточно хорошо определяются края при меньшем количестве вычислений. Внешний вид изобра- жения, полученного с помощью алгоритма Собеля, дает интуитивное ощущение большой точности, которую могут давать нелинейные процессы. Усиление края по Лапласу Метод усиления края по Лапласу отличается от других методов тем, что он не зависит от направления, т. е. края высвечиваются независимо от направления. Это преобразование является приближением лапласиана, известного в матема- тике и электронике. Усиление края по Лапласу образует более резкий край, чем при других алгоритмах усиления. Дополнительно оно высвечивает края, име- ющие как положительные, так и отрицательные изменения яркости. Поэтому усиление края по Лапласу находит применение во многих машинных процессах. Для читателей, знакомых с математикой, известно, что функция /(ж, у) Ла- пласа записывается в виде £(/(я, У)) = d2f/dx2 + d2f/dy2, где d2f/dx2 — вторая частная производная по я, a d2f/dy2 — вторая частная производная по у. Для дискретных функций вторые производные могут быть аппроксимированы следующим образом: tffldx2 = f(x + 1) - •/(*) + /(« - 1) d2 W = f(v + 1) - 2 • f(y) + f(y - 1). Таким образом, лапласиан можно записать в следующем виде: Ь(/(х, у)) = f(x + 1,2/) + f(x — 1,2/) + f(x, у + 1) + f(x, у - 1) - 4 • f(x, у). Это выражение можно рассматривать как ядро свертки, записанное в виде функции /(ж, у), Само ядро при этом имеет вид p(z,2/)= 1 -4 О 1 О На рис. 10.2 ядро обозначено как LAP1. Все операции по усилению ядра, включая лапласианы, ослабляют низкоча- стотные пространственные компоненты изображения. Области постоянной ин- тенсивности или линейно возрастающей интенсивности становятся черными как результат этих преобразований, а области быстро изменяющихся значений ин- тенсивности ярко высвечиваются. Ядра свертки, ослабляющие низкочастотные компоненты, имеют коэффициенты, сумма которых равна нулю.
Свертка, 367 При применении ядер свертки типа лапласиан к различным изображени- ям можно получить некоторые интересные эффекты. Например, постарайтесь соотнести изображение с ядром LAP3. Это имеет эффект прямого добавления свернутых изображений обратно к исходному изображению, которое прибавляет неожиданные детали к неинтересному изображению. Усиление края методом сдвига и разности Этот алгоритм усиливает края изображения, сдвигая изображение на элемент и затем вычитая сдвинутое изображение из исходного. Результатом вычитания является измерение наклона распределения яркости. В области постоянной ин- тенсивности результатом вычитания будет наклон, равный нулю. Нулевой на- клон — это значения черных элементов изображения. В области с большими из- менениями интенсивности, например на крае, вычитание даст большое значение наклона, что приводит к появлению светлого элемента изображения. Чем боль- ше разность интенсивностей, тем более «белым» будет результирующий элемент изображения. При работе с этим алгоритмом необходимо помнить, что могут появляться значения отрицательного наклона с переходом от белого к черному цвету. При этом следует применять абсолютное значение функции, чтобы ал- горитм сдвига и разности мог регистрировать края как переходов от черного к белому, так и от белого к черному. Когда используется это приближение для усиления вертикальных краев, изо- бражение смещается влево на один элемент и затем вычитается из исходного. Для усиления горизонтальных краев изображение смещается вверх на один эле- мент и вычитается. Для усиления горизонтальных и вертикальных краев изо- бражения перед вычитанием изображение сдвигается сначала влево на один эле- мент, а затем вверх. Хотя это приближение кажется простым, его осуществление достаточно сложно. Поэтому вместо фактического сдвига изображения используется сверт- ка для получения того же эффекта. На рис. 10.2 показаны ядра свертки, кото- рые осуществляют усиление края методом сдвига и разности. Эти ядра визуаль- но представляют алгоритм сдвига и разности. Рассмотрим, например, усиление вертикального края. Выше мы говорили, что для усиления вертикальных краев изображение сдвигается влево на один элемент и вычитается. С вертикальным ядром происходит то же самое: сравниваются два горизонтально примыкающих элемента изображения, чтобы найти наклон изменения яркости. Если этот про- цесс проходит по области с постоянной интенсивностью, результат свертки будет равен нулю, потому что —1 ♦ интенсивность + 1 • интенсивность = 0. Если наблюдаются большие изменения интенсивности, результатом будет ли- бо большое положительное число (для перехода от черного к белому), либо боль- шое отрицательное число (для перехода от белого к черному). Интенсивность результирующего элемента будет прямо пропорциональна наклону изменения яркости. При использовании метода усиления края с программой свертки, установите параметр «Absolute» на TRUE, чтобы усиливались переходы как из белого в черный, так и из черного в белый.
368 Глава 10. Пространственные процессы Усиление края методом направленного градиента Усиление края методом сдвига и разности, подробно рассмотренное выше, по- казывает, как можно усилить вертикальные и горизонтальные края в изобра- жении. В реальных программах большинство алгоритмов усиления края, используя только ядро 3x3, усиливают более одной полной вертикальной и горизонталь- ной линии. Ядро большего размера может быть использовано при увеличении требований к вертикальному и горизонтальному краям. Иногда необходимо высветить и другие края изображения, не строго верти- кальные или горизонтальные. Могут быть важны диагональные края. Избира- тельное высвечивание краев в различных направлениях используется иногда для того, чтобы компьютер «получил» общее представление об изображении. Метод направленного градиента как раз используется для этой цели. Для высвечива- ния краев в восьми различных направлениях используются 8 различных ядер свертки. Эти направления называются как стороны света: север, северо-восток, восток, юго-восток, юг, юго-запад, запад, северо-запад. Эти ядра показаны на рис. 10.2. Если существует положительный наклон в направлении ядра, в выходном изображении будет помещен ярко раскрашенный элемент изображения. Интен- сивность выходного элемента изображения будет зависеть от наклона измене- ния яркости. Чем больше наклон, тем ярче элемент. Например, градиент ядра «восток» будет усиливать край, который содержит переход от черного к белому слева направо. На рис. 10.3 показано применение градиентов «север» и «восток». Теперь мы знаем, что если сумма коэффициентов ядра равна нулю, то обла- сти постоянной яркости (низкочастотные пространственные компоненты) будут ослаблены, т. е. области постоянной яркости будут на выходе представлены чер- ными элементами изображения. Существует много специальных ядер свертки для усиления и определения краев изображения. На рис. 10.2 показаны 2 таких ядра, которые называют- ся выравнивающими фильтрами потому, что они напоминают признаки краев, которые они усиливают. Это примеры больших ядер свертки, которые гарантируют более точное опре- деление края за счет уменьшения производительности. Возможны более круп- ные ядра. Они содержат шаблоны или определения контуров предметов, подверг- шихся исследованию в изображении. Если контур соответствует шаблону, края высвечиваются и все другие части изображения становятся черными. Размытие изображения Может показаться, что в обработке изображений размытие изображения умыш- ленно противопоставляется идее усиления изображения. Действительно, размы- тие изображения практически не находит применений в промышленности, хотя этот метод иногда применяется. Например, как художественный прием, размы- тие иногда может быть использовано для того, чтобы создать размытый задний план, на фоне которого помещены предметы переднего плана. Все мы видели свадебные картины с четким изображением невесты и жениха и размытым зад-
Определение краев по Собелю 369 ним планом. Контраст резкости и размытия может быть приятен для глаза. Для этой цели используются фотографические фильтры. Размытие изображения можно получить, используя свертку. На рис. 10.2 показано ядро, используемое для размытия изображения. Это ядро размером 5x5, содержащее единичные элементы. В сущности свертка с ядром размытия усредняет все значения элементов в области примыкания. Усреднение вызывает уменьшение деталей изображения, следствием которого является эффект раз- мытия. Как уже выше говорилось, использование параметра «Scale», передаваемого функции свертки, необходимо для предотвращения насыщения выходного изо- бражения. Это происходит потому, что не существует отрицательных коэффи- циентов свертки для уменьшения эффекта суммы свертки. Значение, выбранное для «Scale», можно определить с помощью визуального наблюдения за резуль- тирующим изображением. Определение краев по Собелю Алгоритм Собеля — единственный нелинейный метод определения края, опи- санный в книге. Этот метод нашел широкое применение. Это другой пример ал- горитма со сложной математической базой, которая относительно проста для выполнения. Фактически имеются 2 различных метода выполнения алгоритма Собеля. В первом методе вычисляются 2 различные свертки: и, исходя из этих сверток, вычисляется величина и направление краев: Величина = Квадратный корень (X2 + У2) Направление = arctg(y/X) Вы можете, вероятно, сказать, что это слишком сложный процесс, чтобы выполнять его для каждого элемента изображения. По этой причине был пред- ложен другой метод выполнения алгоритма Собеля. Для ускорения большого числа вычислений используются различные сокращения, описанные в литерату- ре. Наш метод как раз является одним из них. Метод, который мы будем использовать, очень прост. Во-первых, предполо- жим, что область примыкания точек 3x3 обозначена следующим образом: а Ъ с def g h i Через эту область могут быть проведены только четыре линии, чтобы пере- сечь среднюю точку (е). Эти линии следующие: 24 3
370 Глава 10. Пространственные процессы Линия 1: a-e-i Линия 2: b-e-h. Линия 3: c-e-g Линия 4: d-e-f Каждая линия, прочерченная через область примыкания, делит простран- ство на две трехточечные области. Например, линия 1 делит пространство на области d, g, h и b, с, f. Для каждой из четырех линий вычисляется абсолютное значение разности средних величин двух подобластей. Таким образом, выполняется четыре расче- та полных разностей. Рассматриваемому элементу изображения присваивается самое большое значение из четырех абсолютных разностей. После применения алгоритма Собеля к каждому элементу изображения вы- ходное изображение обычно подвергается обработке пороговым точечным про- цессом. Если новое значение рассматриваемого элемента (только что вычислен- ная самая большая разность) достигает или превышает определенный порог, цвет выходного элемента становится белым. Если значение меньше порога, элемент становится черным. Чистым результатом применения алгоритма Собеля, следу- ющим за точечным пороговым процессом, является черное или белое изображе- ние, не содержащее информации об исходном изображении, исключая информа- цию о крае. На рис. 10.4 показан визуальный эффект этих обработок. Выполнение алгоритма Собеля, описанное в этой главе и в библиотеке функ- ций пространственных процессов, слегка отличается от только что рассмотрен- ного. В основном пороговый точечный процесс выполняется прямо в функ- ции Собеля, а не отдельным преобразованием. В результате повышается про- изводительность: данные изображения не выбираются многократно из буфе- ра. Рабочий режим также был построен на основе функции Собеля, что по- зволяет скопировать информацию об исходном изображении в выходной бу- фер только в том случае, если не достигнуто значение порога. То есть если вычисленная максимальная абсолютная разность значений рассматриваемых элементов выше определенного порога, цвет элемента обязательно становит- ся белым. Если разность меньше значения порога, исходное значение элемен- та не обязательно будет скопировано в выходной буфер изображения. Если это условие, которое контролируется параметром «Overlay», не осуществляет- ся в функции Собеля (табл. 10.1), значение выходного элемента становится черным. Значение, выбранное для порога, влияет на внешний вид изображения. На рис. 10.4 показано определение краев по Собелю. Усредненное фильтрование Усредненное фильтрование — пространственный процесс, который не попада- ет под категорию свертки. Некоторые считают его более точечным процессом, чем пространственным, из-за алгоритма функционирования. Для наших целей усредненное фильтрование считается пространственным процессом. Усредненное фильтрование использует значение элементов, содержащихся в области примыкания, для определения нового значения рассматриваемого эле-
Исходное изображение Рис. 10.4. Определение краев по Собелю. 24*
372 Глава 10. Пространственные процессы Исходное изображение точка со значением 60 Значение 29 заменяет 60 в выходном изображении. Если 60 было значением точки шума, создавшей белое пятно на исходном изображении, это пятно будет отфильтровано. Рис. 10.5. Работа усредняющего фильтра. мента. Однако новое значение элемента не вычисляется алгоритмически из эле- ментов в области примыкания. Вместо этого оно располагает элементы в области примыкания в возрастающем порядке и отбирает средние значения как новые значения рассматриваемых элементов. Алгоритм усредняющего фильтра пока- зан на рис. 10.5. Изображения после усредненного фильтрования показаны на рис. 10.6. Результатом усредненного фильтрования является то, что любой случайный шум, содержащийся в изображении, будет эффективно устранен. Это происхо- дит потому, что любое случайное резкое изменение в интенсивности элемента в пределах области примыкания, будет сортироваться, т. е. оно будет помеще- но либо на вершину, либо на нижнюю часть отсортированных значений области
Исходное изображение, испорченное случайным шумом Изображение, исправленное с помощью медианной фильтрации. Отметьте отсутствие шума. а б Изображение, исправленное спомощью медианной фильтрации. 2 приложение. в Рис. 10.6. Усредненное фильтрование (медианная фильтрация).
374 Глава 10. Пространственные процессы примыкания и не будет учитываться, так как для нового значения элемента всегда отбирается усредненное значение. Отбор случайных значений элементов показан на рис. 10.5. Применение усредненного фильтрования можно увидеть в приятных визу- альных эффектах. В качестве примера смотрите изображение на рис. 10.6,в. Заключение В этой главе мы рассмотрели различные алгоритмы обработки изображения, которые объединяются в категорию пространственных процессов. Большинство этих процессов используют понятие свертки. Пространственные процессы пре- образования, осуществляемые в изображении, контролируются ядром свертки. Приведены многочисленные примеры для различных преобразований. Простран- ственные процессы находят применение как в промышленности, так и в искус- стве. Приведены примеры изображений для того, чтобы показать результаты применений многих пространственных процессов. Применение точечных процес- сов, описанных в гл. 9, и пространственных процессов, описанных в этой главе, расширяет возможности обработки изображений.
Глава 11 Покадровые процессы В главе рассмотрены следующие вопросы: • Покадровые процессы и их отличие от точечных и пространственных про- цессов. • Использование покадровых процессов для наблюдения за изображениями и за их изменением. • Особенности применения покадровых процессов к данным изображения. Введение В предыдущих двух главах мы рассмотрели алгоритмы точечных и простран- ственных процессов обработки изображения. В этих классах алгоритмов исполь- зовалась информация, содержащаяся в изображении и связанная с функцией преобразования, для создания нового изображения. В покадровых процессах, рассмотренных ниже, используется информация от двух (или более) изображе- ний вместе с комбинирующей функцией для создания нового изображения, кото- рое зависит не только от содержания каждого входного изображения, но также от типа функции, используемой для их преобразования. Эти процессы называ- ются покадровыми, а законченное видеоизображение — кадром. Таким образом, покадровые процессы выполняются над двумя полными видеоизображениями. Покадровые процессы применяются во многих приложениях. В промышлен- ности они используются для надежности, контроля качества и повышения каче- ства изображения. Они также применяются в искусстве. Ниже мы опишем не- которые из этих применений. Так, в охранных программах покадровые процессы могут использоваться для детектирования движения, которое может указывать на появление «злоумышленников». Предположим, что видеокамера установле- на в вестибюле здания таким образом, что весь вестибюль оказывается в по- ле ее зрения. Если каждый кадр видеокамеры оцифровывается и сравнивается с предыдущим кадром, в поле зрения камеры может быть обнаружено любое движение. Сравнение видеоизображений — это в действительности вычитание изображений, за которым следует применение порогового процесса и, наконец, подсчет различающихся элементов изображения. Если число элементов разли- чается в двух последовательных видеоизображениях и превышает предопреде- ленное пороговое значение, необходимо включать сигнал тревоги. Обнаружива- ется не только движение, но указываются направление и скорость движения. Это возможно, если известны три параметра: 1. Время между последовательными видеокадрами. 2. Расстояние от камеры до движущегося объекта. 3. Общее расположение объекта в элементах изображения.
376 Глава 11. Покадровые процессы Наличие этой информации и знание тригонометрии позволяет вычислять скорость и направление. Важно, что сравнение всегда выполняется между последовательными видео- кадрами, а не между исходным изображением вестибюля и поступающим ви- деосигналом. Это происходит оттого, что изменение уровня интенсивности света в обозреваемой области в течение дня может быть достаточным, чтобы вызвать тревогу. Использование последовательных видеокадров для сравнения устраняет проблемы, вызванные легким изменением уровня освещенности. Если «злоумыш- ленник» попадет в поле зрения и включит свет, если он до этого был выключен, сразу поднимется тревога, как результат сильных изменений в уровне освещен- ности. Вычитание видеоизображений имеет и другое практическое применение. На рис. 11.1 показан результат удаления фона из изображения. Это полезно в том случае, когда необходимо изучить предмет на переднем плане, а задний план при этом сильно отвлекает (в визуальной или цифровой форме). Если имеются два изображения: на одном — только задний фон, а на другом — предмет на переднем плане с затемненным задним фоном, то вычи- тание используется для удаления всех следов заднего плана. Обратите внима- ние, что процесс вычитания не оставляет объект заднего фона нетронутым. Если часть заднего фона, скрытая объектом переднего плана, содержит яркие точки, предмет переднего фона, оставшийся после процесса вычитания, будет искажен маской из этих ярких точек. Однако, если задний фон в близости от предмета на переднем плане тем- ный, то вычитание изображения заднего и переднего планов будет очень мало влиять на предмет переднего плана. Если задний план создает помехи, то более лучшим методом для выделения объекта на переднем плане является использо- вание маски (рассмотренной ниже в этой главе). Правильно помещенная маска может удалить весь задний фон, окружающий объект на переднем плане, без воздействия на этот предмет. Другим применением вычитания покадровых про- цессов является контроль качества. Рассмотрим поток печатных плат, которые перемещаются по конвейеру и которые необходимо тестировать. Каждый, кто был связан с тестированием печатных плат, знает, насколько этот процесс тру- доемкий и как легко что-то пропустить. Если имеется изображение образцовой печатной платы и для каждой печат- ной платы, движущейся по конвейеру, имеется оцифрованное изображение, то для определения местоположения пропущенной или неудачно поставленной де- тали на плате можно использовать метод вычитания. Этот автоматизированный процесс быстро и качественно проводит тестирование, на которое у работников тратится много времени. Возможно также использование других покадровых процессов для определения того, какие конкретно части искажены. На рис. 11.2 показан в действии процесс наблюдения за печатными платами. Белая перемыч- ка, отсутствующая в печатной плате до контроля, немедленно обнаруживается в результирующем изображении. Возможны и другие функции комбинирования изображений (не только вычи- тание). Программа, предусмотренная в библиотеке функций покадрового процес- са (фактически, только единственная функция «Combinelmages»), обеспечивает каждую из следующих комбинирующих функций : «And», «От», «Хот», «Add», «Subtract», «Multiply», «Divide», «Average», «Maximum value», «Minimum value»
Исходное изображение Исходное изображение (Только фон) Исходное изображение с удаленным фоном Рис. 11.1. Удаление фона.
в Рис. 11.2. Контроль печатных плат.
Введение 379 Таблица 11.1. Комбинирующие функции, имеющиеся в библиотеке функций пока- дрового процесса Комбини- рующие функции Данные исходного изображения Описание комбинирующих функций Данные выходного изображения @DCol,DRow SImage ©SCol,SRow DImage ©DCol,DRow 1. And SData DData DData & = SData DData 2. Or SData DData DData | = SData DData 3. Xor SData DData DData^= SData DData 4. Add SData DData DData 4- = SData DData 5. Sub SData DData DData — = SData DData 6. Mult SData DData DData * = SData DData 7. Div SData DData DData / = SData if SData ! = 0 DData 8. Min SData DData DData = MIN(SData,DData) DData 9. Max SData DData DData = MAX(SData,DData) DData 10. Ave SData DData: DData = (SData-|-DData)/2 DData 11. Overlay SData DData DData = SData DData Примечания 1. «SData» — выбрано из исходного изображения. «DData» — выбрано из выходного изобра- жения и в конечном счете помещено в выходное изображение. 2. Выходное изображение изменяется при воздействии всех этих комбинаций. 3. «Overlay» выполняет копирование из входного в выходное изображение. Таблица истинности основных логических функций Входы And Выходы NXor A В Nand Or Nor Xor 0 0 0 1 0 1 0 1 0 1 0 1 1 0 1 0 1 0 0 1 1 0 1 0 1 1 1 0 1 0 0 1 и «Overlay». Рассмотрим более подробно табл. 11.1. В этой таблице приведены правила для основных комбинирующих функций. С ее помощью можно пред- сказать результат комбинирования перекрывающихся данных изображения, ис- пользуя простые логические функции. Функция поразрядного умножения «And» используется для маскирования части изображения. Сначала продуцируется изображение маски, которое содер- жит значение 1 для каждого сохраняемого элемента изображения и 0 для мас- кируемого элемента изображения (изменяется на черный). Когда изображение и маска комбинируются с помощью функции «And», выходное изображение будет таким же, как и входное изображение в тех элементах изображения, когда мас- ка равна 1, и будет черным, когда маска равна 0. На рис. 11.3 показан эффект маскировки изображения.
Рис. 11.3. Маскировка изображения.
Введение 381 Использование функции «And» с маской, имеющей значения, отличные от О и 1, малоэффективно. Результатом этого будет избирательное удаление битов из каждого значения элемента с изменением его цвета. Практическое приме- нение этого метода неизвестно, хотя возможно использование в искусстве. Ис- пользование функции «And» для комбинирования действительного изображе- ния может также иметь интересный визуальный эффект. Те же эффекты на- блюдаются и при комбинировании изображения с помощью любого логическо- го оператора: «And», «Ог» и «Хог». Функция «Ог», используемая при опреде- ленных условиях, может вполне эффективно сочетать два изображения. Если два изображения имеют яркие предметы на переднем плане и на темном (чер- ном) заднем фоне и не перекрываются при соединении обоих предметов на пе- реднем плане в единое изображение, то может быть использована функция «Ог». Предмет на переднем плане одного изображения перекрывает темный задний фон другого изображения, и наоборот. Функция «Ог» вызывает пере- нос установленных битов объектов переднего плана в выходном изображении, так как любой установленный бит объекта переднего плана, просуммирован- ный с нулевым битом заднего плана, останется установленным. Функция «Ог» подчиняется тем же правилам в комбинации изображения, что и в логиче- ской электронной ячейке. Возникают проблемы, если перекрываются предме- ты переднего плана двух изображений, которые уже скомбинированы. Функ- ция «Ог» двух ненулевых значений элементов в пределах переднего плана бу- дет представлена новым значением элемента, который является комбинацией двух элементов. Цвет, используемый для отображения (в шкале серого цвета) наложенных точек, не будет связан с изображением. Результат не имеет прак- тического применения. На рис. 11.4 показан эффект комбинирования изобра- жений. Функции «Ог» и «Хог» — другие функции комбинирования. Из элементар- ной логики вы помните, что функция «Хог» любые одинаковые биты перево- дит в нулевые, а различающиеся биты в равные 1 (см. табл. 11.1). С помощью этой функции можно определить все элементы изображения с определенным значением. Для этого буфер изображения очищают до определенного значения элемента, например, 32. Если это изображение обрабатывается функцией «Хог» вместе с настоящим изображением, то каждый элемент реального изображения, равный 32, будет установлен на черный, а все другие элементы станут другого цвета (серого). Для усиления эффекта после функции «Хог» можно применить алгоритм точечного процесса, который устанавливает все черные точки в белые и все другие точки в черные (рис. 11.5). Программа показана в листинге 11.2. Отметим, что если сам алгоритм точеч- ного процесса может осуществлять такие же преобразования в однопроходном процессе, это осуществляется путем помещения значения белого цвета (63) в та- блице преобразования в ячейку 32, а во все другие ячейки помещается нулевое значение. Когда изображение преобразуется, используется таблица преобразова- ний. Все элементы исходного изображения со значением 32 станут белыми, а все другие элементы — черными. Арифметическая функция «add» — еще один путь комбинирования изображений, гораздо лучший, чем функция «Ог», описанная выше. Этот метод используется для комбинации изображений, которые накла- дываются одно на другое без странных эффектов (замещение битов), возника-
382 Глава 11. Покадровые процессы Рис. 11.4. Комбинирование изображений. ющих при действии функции «Ог». Если элементы двух изображений добавля- ются друг к другу, подвергаясь действию функции «add», при этом не теряется или искусственно не создается информация (до тех пор пока не произойдет ариф- метическое переполнение). Результирующая интенсивность элемента изображе- ния будет равна сумме значений интенсивности двух образовавших ее элемен- тов. Переполнение элементов изображения — явление, которое надо учитывать при использовании функции «add» для изображений. Переполнение возможно потому, что сумма интенсивностей двух элементов изображения может легко достичь максимально допустимого значения: в нашем случае значения, равного
Введение 383 1 с 2 1 макс 2 1 мин 2 ж Рис. 11.4. (Продолжение) 63. Для предотвращения переполнения функция «Combinelmage» может промас- штабировать интенсивность полученных изображений таким образом, чтобы ней- трализовать переполнение. Параметр «Scale», соответствующий этой функции, контролирует коэффициент масштабирования, использованный для полученно- го изображения. В табл. 11.2 приведена информация о параметре «Scale». Здесь широко используется вычитание, потому что вычитание изображений — одна из наиболее эффективных комбинирующих функций. При использовании функции вычитания необходимо помнить о следующем. Очень часто вычитание изображе- ний приводит в результате к отрицательным значениям элементов изображения,
Данное изображение было очищено до значения интенсивности 32 В Все точки исходного изображения имеют интенсивность 32 Рис. 11.5. Интенсивность элементов изображения.
Введение 385 которые не могут быть продемонстрированы и соответственно переходят в зна- чение, равное нулю. Если ваша программа включает отрицательные интенсивно- сти элементов изображения, то для получения положительных значений и воз- можности их демонстрации используется функция абсолютного значения. Умно- жение и деление изображений являются интересными функциями, результаты которых могут быть весьма необычными при их использовании к соответствую- щему типу изображения, однако практическое применение этих комбинирующих функций не известно. В результате умножения двух изображений усиливается контраст одного изображения, основанного на значениях интенсивности другого. Комбинирующие функции умножения и деления рассмотрены здесь только для полноты описания. При использовании этих функций очень важно масштаби- рование. Значения умноженных или разделенных элементов изображений могут легко выйти за пределы диапазона. Масштабирование используется для возврата значений в нужный диапазон. Комбинирующие функции «Мш»и «Мах» работа- ют так, как и ожидалось. Функция «Min» размещает самое малое значение из двух исходных элементов изображения в выходное изображение. Изображения, полученные с помощью этих функций, размывают перекрытые области исход- ного изображения. Промышленное применение этих комбинирующих функций не известно. Комбинирующая усредняющая функция суммирует значения элементов изо- бражения от двух исходных изображений и делит результат на 2 перед помеще- нием его в выходное изображение. При использовании изображений с различной структурой усредняющая функция также имеет большее художественное при- менение, чем промышленное. Если усредняющая функция используется для усреднения двух идентичных изображений, результатом этого является изображение с меньшим шумом. Дей- ствительно, шум по отношению к любому изображению (в процессе усредне- ния) возрастает как д/N, так как любой случайный шум в изображении после усреднения уменьшается. Шум видеокамеры — первая причина ограниченно- го использования усредняющего метода. Постоянные, не случайные аберрации в сериях изображений не будут улучшаться с помощью усреднения. Комбини- рующая усредняющая функция «Combinelmage» применяется только для двух изображений. Если умноженное изображение необходимо усреднить, нужно со- здать новую функцию. Последней комбинирующей функцией является «Overlay». По существу она обеспечивает прямое копирование данных исходного изображения в выходное изображение без других преобразований. Эта функция может быть использована для копирования предмета с одного изображения на другое или с одного изобра- жения на другую часть этого же изображения. Если исходное и выходное изо- бражения являются одинаковыми, необходимо быть очень внимательным, так как в программе не осуществляется никаких проверок для обнаружения и пред- отвращения наложения. Функция «Translation» из библиотеки функций геоме- трических процессов, описанных в гл. 12, также используется для перемещения предметов вокруг изображения. Программа покадровых процессов, показанная в листинге 11.1, содержит файлы «frprocess.h» и «frprocess.c». 25-3
386 Глава 11. Покадровые процессы Листинг 11.1. Библиотека функций покадрового процесса Ниже приводится содержимое файла frprocess.h: /♦ Header файл ♦/ /* Функции покадровой обработки */ /♦ Разработан в Turbo С 2.0 ♦/ /* Црэйгои А. Линдли */ /♦ ♦/ /♦ Версия: 1.0 */ /* Последние изменения внесены 11/14/89 */ /**♦***♦*♦***♦*************♦♦****♦♦******/ /* Тип комбинации изображений, определяемый пользователем */ typedef enum {And,Or Дог,Add,Sub,Mult,Div,Min,Max,Ave,Overlay} BitFunction; /* Прототипы функций покадровой обработки */ void Combine Images (BYTE huge *SImage, unsigned SCol,unsigned SRow, unsigned SWidth,unsigned SHeight, BYTE huge *DImage, unsigned DCol,unsigned DRow, enum BitFunction CombineType, short Scale); Далее следует текст файла frprocess.c: /*♦**********************************♦♦**/ /♦ Функции покадровой обработки ♦/ /♦ Разработаны в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 11/14/89 */ /*************************♦♦♦*♦**********/ ♦include <stdio.h> ♦include <stdlib.h> ♦include <conio.h> ♦include <dos.h> ♦include <alloc.h> ♦include <process.h> ♦include <math.h> ♦include <graphics.h> ♦include "misc.h” ♦include "pcx.h” ♦include "vga.h” ♦include ”imagesup.h” ♦include "frprocess.h”
Введение 387 /* единственная функция выполняет все комбинации изображений ♦/ void Combinelmages (BYTE huge *SImage, unsigned SCol,unsigned SRow, unsigned SWidth,unsigned SHeight, BYTE huge *DImage, unsigned DCol,unsigned DRow, enum BitFunction CombineType, short Scale) { register unsigned SImageCol,SImageRow,DestCol; short SData,DData; unsigned SColExtent,SRowExtent; if (ParameterCheckOK(SCol,SRow,SCol+SWidth,SRow+SHeight,”Combinelmages”) && ParameterCheckOK(DCol,DRow,DCol+SWidth,DRow+SHeight,”CombineImages”)) { SColExtent = SCol + SWidth; SRowExtent = SRow + SHeight; for(SImageRow = SRow; SImageRow < SRowExtent: SImageRow++) { /♦ переустанавливаем счетчик колонки назначения в начале каждой строки ♦/ DestCol=DCol; for(SImageCol » SCol; SImageCol < SColExtent: SImageCol++) { /* взять байт данных изображений источника и адресата ♦/ SData = GetPixelFromlmage(SImage,SImageCol,SImageRov); DData = GetPixelFromlmage(DImage,DestCol,DRow); /♦ комбинируем данные источника и адресата согласно параметру ♦/ switch (CombineType) { case And: DData &= SData; break; case Or: DData Is SData; break; case Xor: DData *= SData; break; case Add: DData += SData; break; case Sub: DData -= SData; break; case Mult: DData ♦» SData; break; 25*
388 Глава 11. Покадровые процессы Листинг 11.1. (Продолжение) case Div: if(SData ! = 0) DData /= SData; break; case Min: DData = MIN(SData,DData) ; break; case Max: DData = MAX(SData,DData) ; break; case Ave: DData = (DData+SData)/2; break; case Overlay: DData » SData; break; /♦ Масштабируем результирующие данные, если требуется. Положительное значение смещает выходные данные вправо, вследствие чего осуществляется уменьшение по степеням двойки. Нулевое значение оставляет данные без изменений. Отрицательное значение сдвигает данные влево, осуществляя увеличение по степеням двойки. ♦/ if(Scale < 0) DData «в abs(Scale); else if (Scale > 0) DData »e Scale; /* He позволяем данным элемента изображения выйти за пределы */ DData = (DData < MINSAMPLEVAL) ? MINSAMPLEVAL-.DData; DData « (DData > MAXSAMPLEVAL) ? MAXSAMPLEVAL:DData; Put Pixel InImage (DImage,DestCo1++,DRow,DData); /* переходим к следующей строке в изображении адресата*/ DRov++; Дополнительно дан пример программы, использованной для создания всех изображений в этой главе. Она включает файлы «frtest.c» и «frtest.prj» (листинг 11.2).
Введение 389 Листинг 11.2. Пример программы покадрового процесса Ниже приводится текст файла frtest.prj: graphics.lib vgagraph. obj egavga.obj pcx (misc.h pcx.h) vga (mics.h vga.h pcx.h) imagesup (mics.h vga.h psx.h imagesup.h) ptprocess (mics.h vga.h psx.h imagesup.h,ptprocess.h) frprocess (mics.h vga.h psx.h imagesup.h,frprocess.h) frtest (mics.h vga.h psx.h imagesup.h ptprocess.h,frprocess.h) Далее следует текст файла frtest.с: /****************************************/ /♦ Демонстрационная программа ♦/ /♦ покадровых методов обработки ♦/ /♦ изображения ♦/ /♦ Разработана в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ */ */ /* /♦ Версия: 1.0 /♦ Последние изменения внесены 12/26/89 */ #include <stdio.h> #include <conio.h> #include <dos.h> #include <alloc.h> #include <process.h> ♦include <graphics.h> ♦include “misc.h*’ ♦include “pcx.h" ♦include “vga.h" ♦include “imagesup.h“ ♦include “ptprocess.h“ ♦include “frprocess.h" /* демонстрационная программа покадровой обработки */ void main(void) { char *InFileNamel = “p2cllila“; char *InFileName2 = “p2cllilb“; char *InFileName3 ж “p2clli2a“; char *InFileName4 s “p2clli2b“; char *InFileName5 = “p2clli3a“; char *InFileName6 = “p2clli4a“; char *InFileName7 » “p2clli4b“; char *InFileName8 « “p2clli5a“;
390 Глава 11. Покадровые процессы Листинг 11.2. (Продолжение) BYTE huge ♦ In I mage 1; BYTE huge *InImage2; BYTE huge «Buffer; unsigned GenPCXFiles « FALSE; /♦ управляет созданием выходного файла ♦/ BYTE LUT[MAXQUANTLEVELS}; unsigned Index; /* Инициализация графической подсистемы */ InitGraphics (); printf ("Frame Transofrm Example Program\n\n"); printf ("Reading the Image PCX File into memory\n"); /♦ Запрашиваем столько памяти, сколько нужно, чтобы спасти изображение, полученное в результате каждой трансформации ♦/ Buffer » (BYTE huge ♦) farcalloc(RASTERSIZE,sizeof(BYTE)); if(Buffer == NULL) { printf("FRTEST Error:No memory for buffer\n"); exit(ENoMemory); } /♦ Последовательность изображений на рис. 11.1 - Удаление фона ♦/ /♦ Читаем изображение источника и адресата из памяти. Изображение, прочитанное в Inlmage2, рассматриваем как изображение адресата. ♦/ if(ReadPCXFileToBuf(InFileName1,&lnlmagel) != NoError) exit(1); DisplaylmagelnBuf(Inlmagel,INITVGALOADPALETTE,WAITFORKEY); if(ReadPCXFileToBuf(InFileName2,&InImage2) != NoError) exit(1); DisplaylmagelnBuf(Inlmage2,NOVGAINIT,WAITFORKEY); /♦ Комбинируем изображение источника с изображением адресата с помощью вычитания. Переписываем полученное изображение. ♦/ Combinelmages (Inlmage2 ,MINCOLNUM,MINROWNUM,MAXCOLS, MAXROWS, Inlmagel,MINCOLNUM,MINROWNUM,Sub,O); /♦ Показываем на экране изображение с удаленным фоном ♦/ DisplaylmagelnBuf(Inlmagel,NOVGAINIT,WAITFORKEY); if(GenPCXFiles)
Введение 391 WritePCXFile(”p2cl0i1c",8,320,200,1,320); /♦ Возвращаем память, взятую под буфер изображения ♦/ farfree((BYTE far ♦) Inlmagel); farfree((BYTE far *)InImage2); /♦ Последовательность изображений на рис. 11.2 - Контроль печатных плат ♦/ /♦ Читаем изображение источника и адресата из памяти. Изображение, прочитанное в Inlmage2, рассматриваем как изображение адресата. */ if (ReadPCXFileToBuf (InFileName3,&InImagel) != NoError) exit(1); DisplayImagelnBuf(Inlmagel,NOVGAINIT,WAITFORKEY); if(ReadPCXFileToBuf(InFileName4,&InImage2) != NoError) exit(1); DisplayImagelnBuf(Inlmage2,NOVGAINIT,WAITFORKEY); /* Комбинируем изображение источника с изображением адресата с помощью вычитания. Переписываем полученное изображение. ♦/ Combinelmages (Inlmage2, MINCOLNUM, MINROWNUM, MAXCOLS, MAXROWS, Inlmagel,MINCOLNUM,MINROWNUM,Sub,0); /* Показываем различия между изображениями ♦/ DisplayImagelnBuf(Inlmagel,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile(”p2c1li2c”,8,320,200,1,320); /* Последовательность изображенийна рис. 11.3 - Маскировка изображения */ /* Считываем в маскируемое изображение ♦/ if(ReadPCXFileToBuf(InFileName5,&lnlmagel) != NoError) exit(l); DisplayImagelnBuf(Inlmagel,NOVGAINIT,WAITFORKEY); /* Приготавливаем маску изображения */ ClearImageArea(Buffer,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS,WHITE); ClearImageArea(Buffer,200,10,80,180,BLACK); /♦ Показ маски ♦/ DisplayImagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile(”p2clli3b”,8,320,200,1,320);
392 Глава 11. Покадровые процессы Листинг 11.2. (Продолжение) /* Осуществляем маскирование */ Combine Images (Buffer ,MINCOLNUM,MINROWNUM,MAXCOLS, MAXROWS, Inlmagel,MINCOLNUM,MINROWNUM,And,0); DisplaylmagelnBuf(InImage1,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2c1li3c",8,320,200,1,320); /* Возвращаем память, взятую под буферы изображений ♦/ farfree((BYTE far *)lnlmagel); farfree((BYTE far *)InImage2); /♦ Последовательность изображений на рис. 11.4 - Комбинирование изображений */ /* Считываем изображение источника и адресата в память. Изображение, прочитанное в Inlmage2, рассматриваем как изображение адресата. */ if(ReadPCXFileToBuf(InFileName6,&InImagel) != NoError) exit(l); DisplaylmagelnBuf(Inlmagel,NOVGAINIT,WAITFORKEY); if(ReadPCXFileToBuf(InFileName7,&InImage2) != NoError) exit(l); DisplaylmagelnBuf(Inlmage2,NOVGAINIT,WAITFORKEY); /* Делаем копию изображения адресата так, чтобы его можно было восстановить без загрузки с диска. */ Copyimage (Inlmage2 ,Buf f er); /* Комбинируем изображение источника с изображением адресата с помощью сложения. Переписываем полученное изображение. */ Combinelmages(Inlmagel,32,20,256,160, InImage2,32,20,Add, 1); DisplaylmagelnBuf(Inlmage2,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile(”p2c1li4c",8,320,200,1,320); /* Востанавливаем Inlmage2 из буфера адресата для использования следующего покадрового процесса обработки. Сейчас мы вернулись назад в наше первоначальное положение с изображением источника в Inlmagel и изображением адресата в Inlmage2. */
Введение 393 Copyimage (Buffer,InImage2); /♦ Комбинируем изображение источника с изображением адресата с помощью OR процесса. Переписываем полученное изображение. ♦/ CombineImages(InImage1,32,20,256,160, InImage2,32,20,0r,0); DisplayImagelnBuf(Inlmage2,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2clli4d",8,320,200,1,320); CopyImage(Buffer,Inlmage2); /* Комбинируем изображение источника с изображением адресата с помощью усреднения. Переписываем полученное изображение. */ CombineImages(InImage1,32,20,256,160, Inlmage2,32,20,Ave,0); DisplayImagelnBuf(Inlmage2,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2c1li4e",8,320,200,1,320); CopyImage(Buffer,Inlmage2); /* Комбинируем изображение источника с изображением адресата с помощью выбора максимального из них. Переписываем полученное изображение. */ Combinelmages(InImage1,32,20,256,160, Inlmage2,32,20 ,Мах, 0) ; DisplayImagelnBuf(Inlmage2,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile(”p2c1li4f",8,320,200,1,320); CopyImage(Buffer,Inlmage2); /* Комбинируем изображение источника с изображением адресата с помощью выбора минимального из них. Переписываем полученное изображение. */ Comb inelmagesdnlmagel ,32,20,256,160, InImage2,32,20,Min,0); DisplayImagelnBuf(Inlmage2,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2c1li4g",8,320,200,1,320); CopyImage(Buffer,Inlmage2);
394 Глава 11. Покадровые процессы Листинг 11.2. (Продолжение) /* Возвращаем память, взятую под буферы изображения ♦/ farfree((BYTE far *)Inlmagel); farfree((BYTE far *)InImage2); /♦ Последовательность изображений на рис. 11.5 - Интенсивность элементов изображения ♦/ if (ReadPCXFileToBuf (InFileName8,&lnlmagel) != NoError) exit(1); DisplayImagelnBuf (Inlmagel,NOVGAINIT,WAITFORKEY) ; /♦ Все байты буфера Inlmage2 устанавливаем равными 32 ♦/ ClearImageArea(Buffer,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS,32); DisplayImagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2clli5b",8,320,200,1,320); /♦ Комбинируем изображение источника с изображением адресата с помощью Хог-процесса. Переписываем полученное изображение. */ Combinelmages(Inlmagel,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, Buffer,MINCOLNUM,MINROWNUM,Xor,0); /♦ После операции Xor все точки, которые имели значение 32, стали черными, остальные приобрели другие цвета. Используем теперь точечную обработку, чтобы преобразовать черные точки в белые и все другие в черные. */ for(Index=0;Index < MAXQUANTLEVELS; Index++) LUT[Index] » 0; LUT[O] » MAXSAMPLEVAL; PtTransform(Buffer,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS,LUT); DisplayImagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2clli5c",8,320,200,1,320); /♦ Освобождаем всю занятую память ♦/ farfree((BYTE far *)lnlmagel); farfree((BYTE far *)Buffer); restorecrtmode(); closegraph();
Введение 395 Программа использования библиотеки функций покадрового процесса долж- на содержать файл «frprocess.h» и должна быть в дальнейшем включена в «frprocess.c». Файлы «frtest.с» и «frtest.prj» используются как модули типичного применения программы. Прототипы функций и описание покадрового процесса, содержащиеся в библиотеке функций, приведены в табл. 11.2. Таблица 11.2. Библиотека функций покадрового процесса Функция комбинирования изображений Прототип void Combinelmages (BYTE huge *SImage, unsigned SCol, unsigned SRow, unsig- ned SWidth, unsigned SHeight, BYTE huge *DImage unsigned DCol, unsigned DRow, enum BitFunction CombineType, short Scale); где «SImage» — указатель на исходное изображение. Исходное изображение мо- жет комбинироваться с выходным изображением, но исходное изображение ни- когда не будет испорчено. «SCol», «SRow», «SWidth» и «SHeight» — параметры, контролирующие об- ласть исходного изображения, которая используется в преобразовании выходного изображения. «SWidth» и «SHeight» описывают размеры прямоугольной области изображения с «SCol» и «SRow», отсчитывающихся с левого верхнего угла обла- сти изображения. В выходном изображении будет преобразована область такого же размера. «DImage» — указатель на выходное изображение. Исходное изображение (или часть его) вместе с комбинирующей функцией преобразуют это выходное изобра- жение. Выходное изображение всегда описывается с помощью процессов преобра- зований. «DCol» и «DRow» — параметры, контролирующие область выходного изо- бражения, которая будет подвержена процессу преобразования. Область изобра- жения, которая будет преобразована, контролируется с помощью «SWidth» и «SHeight», а позиция элемента изображения в выходном буфере контролируется «DCol» и «DRow». «CombineType» — параметр, который контролирует, какую из комбиниру- ющих функций применить к данному изображению. Используются следующие типы: «And», «От», «Хот», «Add», «Sub(tract)», «Mult(iply)», «Div(ide)», «Ave- rage)», «Max(imum value)», «Min(imum value) и «Overlay». (Смотрите в тексте описание действия каждой из этих комбинирующих функций). «Scale» — контролирует как и насколько должно быть промасштабирова- но значение элемента изображения, которое обрабатывается с помощью ком- бинирующих функций, перед помещением его в выходное изображение. Значе- ние, равное нулю, означает, что масштабирование отсутствует. Вычисленные зна- чения элементов изображения хранятся прямо в буфере выходного изображе- ния. Положительное и отрицательное значения «Scale» означают, в каком на- правлении (соответственно вправо или влево) и сколько сдвигов применяется к данным до перемещения их в выходное изображение. В приведенной ниже та- блице показаны параметры операции «Scale». Принято, что значение элементов равно 1.
396 Глава 11. Покадровые процессы Таблица 11.2. (Продолжение) Значение «Scale» Число правых сдвигов Число левых сдвигов Результирующее значение -5 0 5 I * 32 -4 0 4 I ♦ 16 -3 0 3 I * 8 -2 0 2 I * 4 -1 0 1 I * 2 0 0 0 I 4-1 1 0 I ♦ 2 4-2 2 0 1/4 4-3 3 0 1/8 4-4 4 0 I / 16 4-5 5 0 1/32 Таким образом, побитовый контроль осуществляется для того, чтобы вычи- сленное значение элемента изображения находилось в пределах правильного диа- пазона. Действие Действие функции очень просто. Как обычно, два вложенных цикла контро- лируют движение по буферам изображений. Внутри двух циклов выбираются исходное и выходное изображения и затем преобразуются в соответствии с ком- бинирующей функцией. Новое значение элемента изображения, которое получа- ется при комбинировании, произвольно масштабируется и затем перемещается в диапазон интенсивности элемента изображения от 0 до 63. Это новое значе- ние элемента изображения впоследствии помещается в выходное изображение в определенную позицию. Заключение В этой главе мы рассмотрели эффект применения алгоритма покадрового про- цесса к данным изображения и несколько применений этого алгоритма. По воз- можности мы указывали недостатки использования некоторых комбинирующих функций. Пример программы иллюстрирует действие функций покадровых про- цессов.
Глава 12 Геометрические процессы В главе рассмотрены следующие вопросы: • Геометрические процессы и их отличие от точечных, пространственных и покадровых процессов. • Использование геометрических процессов. ♦ Интерполяция элемента изображения. • Соотношение геометрических размеров и методов коррекции. • Масштабирование изображения, вращение, сдвиг и зеркальное отражение. Введение Геометрические процессы — последний класс алгоритмов обработки изображе- ния, которые мы рассмотрим. Геометрические процессы изменяют простран- ственное местоположение и/или структуру элементов в изображении, основыва- ясь на некоторых геометрических преобразованиях. Геометрические преобразо- вания необязательно изменяют значения интенсивности элементов изображения, они изменяют положение элементов изображения. Другими словами, значения интенсивности для данных элементов изображения (информация, содержаща- яся в элементе изображения) перемещается в новую позицию. В этом состоит основное отличие геометрических процессов от точечных, пространственных и покадровых процессов. Каждый из этих классов алгоритмов обработки изобра- жения намеренно изменял значение интенсивности элементов изображения, ис- пользуя определенные преобразования. Геометрические преобразования широко применяются как в промышленности, так и в искусстве. Они могут быть исполь- зованы: 1. Для исправления некоторых дефектов, свойственных системе изображе- ния (пространственные аберрации в датчиках, соотношения геометриче- ских размеров и т. д.). 2. Как предварительный шаг в подготовке изображения для последующей точечной, пространственной или покадровой обработки. 3. Для обеспечения точечной регистрации изображения между изображени- ями, которые будут сравниваться. 4. Как средство конструирования изображения при подготовке его вывода на дисплей или принтер (например, получение комбинированного изобра- жения). 5. Для обеспечения различных эффектов в изображениях. Этот список без всяких сомнений можно продолжить.
398 Глава 12. Геометрические процессы Как и в выше рассмотренных формах обработки изображения, при геометри- ческой обработке также возникает ряд трудностей. Когда изменяются геометри- ческие представления изображений, результат может быть совершенно неожи- данным до тех пор, пока не будут учтены расположения элементов изображения, соотношения геометрических размеров и перезапись буфера. Расположение эле- ментов изображения и соотношение геометрических размеров очень важны для осуществления геометрических преобразований, каждое из которых будет по- дробно рассмотрено. Тестовые изображения иллюстрируют, как важно соблю- дать точное расположение элементов изображения и соотношение геометриче- ских размеров. Подробно рассмотрены проблемы, возникающие при наложении исходного и выходного буфера, и способы их решения. Библиотека функций геометрических процессов, описанная в этой главе, пре- дусматривает масштабирование изображения, размерность, вращение, сдвиг и зеркальное отражение. Геометрические преобразования, предусмотренные эти- ми функциями, становятся очевидными из их названий. Способности манипу- ляции изображением в сочетании со способностями, описанными в гл. 9, 10, 11, образуют полную библиотеку функций обработки изображения. С помощью этих функций и мощного персонального компьютера вы сможете применять методы обработки изображения в различных отраслях — от астрономии до настольных типографий. Программа геометрических процессов, описанная в этой части, показана в листинге 12.1 и содержит файлы geprocess.h и geprocess.c. Пример программы, используемой для создания всех тестовых изображений, показан в листинге 12.2. Программа применения, которая использует библиотеку функций геометриче- ских процессов, должна включать файл geprocess.h и впоследствии должна быть связана с geprocess.c. Используйте getest.c и getest.prj как модули для типичных программ. Про- тотипы функций и описания каждого из геометрических процессов приведены в табл. 12.1. Листинг 12.1. Библиотека функций геометрических процессов Ниже приводится содержимое файла geprocess.h: /****************************************/ /♦ Header файл ♦/ /♦ Функции геометрической обработки ♦/ /♦ Разработан в Turbo С 2.0 ♦/ /♦ Крэйгои А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 11/14/89 ♦/ /****************************************/ /♦ Набор определяемых пользователем типов ♦/ typedef enum {HorizMirror,VertMirror} MirrorType; /♦ Прототипы функций геометрической обработки ♦/
Введение 399 void ScaleImage(BYTE huge ♦ InImage «unsigned SCol «unsigned SRou, unsigned SVidth,unsigned SHeight, double ScaleH«double ScaleV, BYTE huge ♦OutImage» unsigned DCol «unsigned DRov, unsigned Interpolate); void SizeImage(BYTE huge ♦InImage«unsigned SCol«unsigned SRov, unsigned SVidth«unsigned SHeight, BYTE huge ♦OutImage» unsigned DCol«unsigned DRov, unsigned DVidth«unsigned DHeight« unsigned Interpolate); void Rotateimage (BYTE huge ♦InImage «unsigned Col «unsigned Rov, unsigned Vidth,unsigned Height «double Angle» BYTE huge ♦OutImage«unsigned Interpolate); void Trans late Image (BYTE huge ♦InImage» unsigned SCol«unsigned SRov, unsigned SVidth«unsigned SHeight» BYTE huge ♦OutImage» unsigned DCol,unsigned DRov, unsigned EraseFlag); void MirrorImage(BYTE huge ♦InImage, unsigned SCol,unsigned SRov, unsigned SVidth,unsigned SHeight, enum MirrorType VhichMirror, BYTE huge ♦Outlmage, unsigned DCol,unsigned DRov); Далее следует текст файла geprocess.c: I ф♦ * *♦ ♦ ♦♦ ♦♦ ♦ ♦ ♦ ♦ ♦♦ ♦ ♦ ♦ ♦♦ ♦ ♦ ♦ ♦♦ ♦♦ ♦ ♦♦ ♦ ♦♦ ♦♦ ♦ ♦♦ ♦/ /♦ Функции геометрической обработки ♦/ /♦ Разработаны в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 11/14/89 ♦/ /ффффффффффффффф***ф*фффффффффффффффффффф/ finclude <stdio.h> # include <conio.h> # include <dos.h> # include <alloc.h> # include <process.h> # include <math.h> # include <graphics.h> finclude "misc.h" finclude "pcx.h" finclude "vga.h" finclude "imagesup.h"
400 Глава 12. Геометрические процессы Листинг 12.1. (Продолжение) void ScaleImage(BYTE huge ♦InImage,unsigned SCol,unsigned SRow, unsigned SWidth,unsigned SHeight, double SealeH,double SealeV, BYTE huge ♦OutImage, unsigned DCol,unsigned DRow, unsigned Interpolate) { unsigned DestWidth,DestHeight; unsigned PtA,PtB,PtC,PtD,PixelValue; register unsigned SPixelColNum,SPixelRovNum,DestCol,DestRov; double SPixelColAddr,SPixelRovAddr; double ColDelta,RowDelta; double ContribFromAandB,ContribFromCandD; DestWidth « ScaleH ♦ SWidth + 0.5; DestHeight » ScaleV ♦ SHeight**- 0.5; if (ParameterCheckOK(SCol,SRow,SCol+SWidth,SRov+SHeight,”ScaleImage”) && ParameterCheckOK(DCol,DRow,DCol+DestWidth,DRow+DestHeight, ’’ScaleImage”)) { /♦ Вычисления из перспективы выходного изображения ♦/ for(DestRov » 0; DestRov < DestHeight; DestRow++) SPixelRovAddr - DestRow/ScaleV; SPixelRowNum e (unsigned) SPixelRovAddr/ScaleV; RovDelta = SPixelRovAddr - SPixelRowNum; SPixelRovNum += SRov; for(DestCol e 0; DestCol < DestWidth; DestCol++) SPixelColAddr » DestCol/ScaleH; SPixelColNum e (unsigned) SPixelColAddr/ScaleV; ColDelta e SPixelColAddr - SPixelColNum; SPixelColNum += SCol; if(Interpolate) { /♦ SPixelColNum и SPixelRovNum сейчас содержат координаты ближайшей левой верхней от рассматриваемой (точка X) точки Это точка на рис. ниже: А В X С D Мы должны найти уровень яркости каждой из четырех точек, чтобы вычислить значение точки, которое нужно поместить в выходное изображение. Возьмем яркость точки А, так как она уже находится внутри рассматриваемой области входного
Введение 401 изображения. Проверяем, чтобы убедиться, что и другие точки также находятся внутри. Если так, то используем их значения для вычислений. Иначе приравниваем все значения к значению в А. Это приводит к ошибке, но только на краю данного изображения. ♦/ PtA = GetPixelFromlmage(InImage,SPixelColNum,SPixelRowNum); if(((SPixelColNum+1) < MAXCOLS) && ((SPixelRowNum+1) < MAXROWS)) PtB = GetPixelFromlmage(InImage,SPixelColNum+1,SPixelRowNum); PtC = GetPixelFromlmage(InImage,SPixelColNum,SPixelRowNum+1); PtD = GetPixelFromlmage(InImage,SPixelColNum+1,SPixelRowNum+1); } else { /* Все точки имеют равную яркость */ PtB=PtC=PtD=PtA; } /* Интерполируем, чтобы найти вклад каждой точки из области примыкания. Выполняем это в горизонтальном и вертикальном направлениях. */ ContribFromAandB = ColDelta*((double)PtB - PtA) + PtA; ContribFromCandD = ColDelta*((double)PtD - PtC) + PtC; PixelValue = 0.5 + ContribFromAandB + (ContribFromCandD - ContribFromAandB)*RowDelta; } else PixelValue=GetPixelFromImage(InImage,SPixelColNum,SPixelRowNum); /* Помещаем точку в выходное изображение ♦/ PutPixellnlmage(OutImage,DestCol+DCol,DestRow+DRow,PixelValue); } } } } void Sizeimage(BYTE huge *InImage,unsigned SCol,unsigned SRow, unsigned SWidth,unsigned SHeight, BYTE huge *0utImage, unsigned DCol,unsigned DRow, unsigned DWidth,unsigned DHeight, unsigned Interpolate) { double HScale,VScale; /* Проверяем параметры на выход из пределов */ if (ParameterCheckOK(SCol,SRow,SCol+SWidth,SRow+SHeight,"Scalelmage") && ParameterCheckOK(DCol,DRow,DCol+DWidth,DRow+DHeight,“Scalelmage")) { 26-3
402 Глава 12. Геометрические процессы Листинг 12.1. (Продолжение) /♦ Вычисляем горизонтальный и вертикальный масштабные факторы так, чтобы соответствующая часть входного изображения помещалась в соответствующую ей часть выходного изображения. ♦/ HScale = (double)DVidth/(double)SVidth; VScale = (double)DHeight/(double)SHeight; /♦ Вызываем Scaleimage для выполнения остальной работы */ Scalelmage(InImage,SCol,SRov,SVidth,SHeight,HScale,VScale, OutImage,DCol,DRov,Interpolate); } } void Rotateimage (BYTE huge ♦ InImage, unsigned Col,unsigned Rov, unsigned Vidth,unsigned Height,double Angle, BYTE huge ♦OutImage,unsigned Interpolate) { register unsigned ImageCol,ImageRov; unsigned CenterCol,CenteRov,SPixelColNum,SPixelRovNum; unsigned ColExtent,RovExtent,PixelValue; unsigned PtA,PtB,PtC,PtD; double DP ixelRelat iveColNum,DPixelRelat iveRovNum; double CosAngle,SinAngle,SPixelColAddr,SPixelRovAddr; double ColDelta,RovDelta; double ContribFromAandB,ContribFromCandD; if (ParameterCheckOK(Col,Rov,Col+Vidth,Rov+Height,“Rotatelmage“) && { /♦ Угол должен быть равным 0..359.9 ♦/ vhile(Angle >= 360.0) Angle -» 360.0; /♦ Преобразуем единицы измерения угла ♦/ Angle ♦= ((double)3.14159/(double) 180.0); /♦ Вычисляем значения угла для вращения ♦/ CosAngle « cos(Angle); SinAngle = sin(Angle); /♦ Центр вращения ♦/ CenterCol = Col + Vidth/2; CenterRov = Rov + Height/2; ColExtent = Col + Vidth; RovExtent = Rov + Height; /♦ Все вычисления выполняются из перспективы выходного изображения. Абсолютные координаты точки должны быть преобразованы в дюймы в соответствии с размерами дисплея, чтобы сохранить корректные
Введение 403 соотношения между частями изображения при вращении. После вращения вычисленные относительно дисплея расстояния преобразуются обратно в реальные координаты точки. */ for(ImageRow = Row; ImageRow < RowExtent; ImageRow++) DPixelRelativeRowNum » (double)ImageRow - CenterRow; /* Преобразуем номер строки в расстояние на дисплее от центра изображения ♦/ DPixelRelativeRowNum ♦= LRINCHESPERPIXELVERT; for(ImageCol а Col; ImageRow < ColExtent; ImageCol++) DPixelRelativeColNum a (double)ImageCol - CenterCol; /♦ Преобразуем номер колонки в расстояние на дисплее от центра изображения */ DPixelRelativeColNum ♦= LRINCHESPERPIXELHORIZ; /♦ Вычисляем координаты точки источника из позиций точки выходного изображения ♦/ SPixelColAddr = DPixelRelativeColNum*CosAngle- DPixelRelativeRowNum*SinAngle; SPixelRowAddr a DPixelRelativeColNum*SinAngle+ DPixelRelativeRowNum*CosAngle; /* Преобразуем из координат относительно центра изображения в абсолютные координаты. */ /♦ Преобразуем расстояние на дисплее в позиции точки ♦/ SPixelColAddr ♦= LRPIXELSPERINCHHORIZ; SPixelColAddr += CenterCol; SPixelRowAddr ♦= LRPIXELSPERINCHVERT; SPixelRowAddr += CenterRow; SPixelColNum = (unsigned) SPixelColAddr; SPixelRowNum a (unsigned) SPixelRowAddr; ColDelta a SPixelColAddr - SPixelColNum; RowDelta = SPixelRowAddr - SPixelRowNum; if(Interpolate) /♦ SPixelColNum и SPixelRowNum сейчас содержат координаты ближайшей левой верхней от рассматриваемой (точка X) точки. Это точка на рис. ниже: А В X С D 26*
404 Глава 12. Геометрические процессы Листинг 12.1. (Продолжение) Мы должны найти уровень яркости каждой из четырех точек, чтобы вычислить значение точки, которое нужно поместить в выходное изображение. Возьмем яркость точки А, так как она уже находится внутри рассматриваемой области входного изображения. Проверяем, чтобы убедиться, что и другие точки также находятся внутри. Если так, то используем их значения для вычислений. Иначе приравниваем все значения к значению в А. Это приводит к ошибке, но только на краю данного изображения. ♦/ PtA = GetPixelFromlmage(InImage,SPixelColNum,SPixelRowNum); if(((SPixelColNum+1) < MAXCOLS) && ((SPixelRowNum+1) < MAXROWS)) { PtB = GetPixelFromlmage(InImage,SPixelColNum+1,SPixelRowNum); PtC = GetPixelFromlmage(InImage,SPixelColNum,SPixelRowNum+1); PtD = GetPixelFromlmage(InImage,SPixelColNum+1,SPixelRowNum+1); } else { /♦ Все точки имеют равную яркость ♦/ PtB=PtC=PtD=PtA; } /♦ Интерполируем, чтобы найти вклад каждой точки из области примыкания. Выполняем это в горизонтальном и вертикальном направлениях. */ ContribFromAandB = ColDelta*((double)PtB - PtA) + PtA; ContribFromCandD » ColDelta*((double)PtD - PtC) + PtC; PixelValue = 0.5 + ContribFromAandB + (ContribFromCandD - ContribFromAandB)*RowDelta; } else PixelValue=GetPixelFromImage(InImage,SPixelColNum,SPixelRowNum); /♦ Помещаем точку в выходное изображение ♦/ PutPixellnlmage(OutImage,ImageCol,ImageRow,PixelValue); } /♦ Предупреждение: изображения не должны перекрываться ♦/ void TranslateImage(BYTE huge ♦InImage, unsigned SCol,unsigned SRow, unsigned SWidth,unsigned SHeight, BYTE huge *0utImage, unsigned DCol,unsigned DRow, unsigned EraseFlag)
Введение 405 { register unsigned SInageCol,SInageRow,DestCol; unsigned SColExtent,SRowExtent; /* Проверяем параметры на выход из пределов ♦/ if (ParaneterCheckOK(SCol,SRow,SCol+SWidth,SRow+SHeight,“Translatelnage") && ParaneterCheckOK(DCol,DRow,DCol+SWidth,DRow+SHeight,“Translatelnage")) { SColExtent = SCol + SWidth; SRowExtent = SRow + SHeight; for(SInageRow = 0; SInageRow < SRowExtent; SInageRow++) { /♦ Переустанавливаем счетчик колонки адресата перед каждой строкой */ DestCol = DCol; for(SInageCol = 0; SInageCol < SColExtent; SInageCol++) { /* Переносим байт данных изображения между буферами */ PutРixelInInage(OutInage,DestCol++,DRow, GetPixelFronlnage(InInage,SInageCol,SInageRow)); } /♦ Переходим к следующей строке выходного изображения ♦/ DRow++; /♦ Если задано стирание, чистим исходное изображение ♦/ if(EraseFlag) Clе arInageArеа(InInage,SCol,SRow,SW idth,SHe ight,BLACK); void Mirror Inage (BYTE huge *lnlnage, unsigned SCol,unsigned SRow, unsigned SWidth,unsigned SHeight, enun MirrorType WhichMirror, BYTE huge *0utInage, unsigned DCol,unsigned DRow) { register unsigned SInageCol,SInageRow,DestCol; unsigned SColExtent,SRowExtent; /♦ Проверяем параметры на выход из пределов ♦/ if (ParaneterCheckOK(SCol,SRow,SCol+SWidth,SRow+SHeight,“Mirrorlnage”) && ParaneterCheckOK(DCol,DRow,DCol+SWidth,DRow+SHeight,“Mirrorlnage")) { SColfcxtent s SCol + SWidth; SRowExtent = SRow + SHeight; switch(WhichMirror) { case HorizMirror:
406 Глава 12. Геометрические процессы Листинг 12.1. (Продолжение) for (SImageRow = 0; SImageRow < SRowExtent; SImageRow++) { /* Переустанавливаем счетчик колонки адресата перед каждой строкой */ DestCol DCо1+SWidth; for (SImageCol = 0; SImageCol < SColExtent; SImageCol++) { /♦ Переносим байт данных изображения между буферами */ PutPixellnlmage(Outlmage,—DestCol,DRow, GetPixelFromlmage(InImage,SImageCol,SImageRow)); } /* Переходим к следующей строке выходного изображения ♦/ DRow++; break; case VertMirror: DRow += (SHeight-1); for(SImageRow « 0; SImageRow < SRowExtent; SImageRow++) { /* Переустанавливаем счетчик колонки адресата перед каждой строкой */ DestCol « DCol; for(SImageCol = 0; SImageCol < SColExtent; SImageCol++) { /♦ Переносим байт данных изображения между буферами ♦/ PutPixellnlmage(Outlmage,DestCol++,DRow t GetPixelFromlmage(InImage,SImageCol,SImageRow)); /* Переходим к следующей строке выходного изображения ♦/ DRow—; break; Листинг 12.2. Пример программы геометрических функций Ниже приводится текст файла getest.prj: graphics.lib vgagraph.obj egavga.obj pcx (misc.h pcx.h) vga (mics.h vga.h pcx.h) imagesup (mics.h vga.h psx.h imagesup.h) geprocess (mics.h vga.h psx.h imagesup. h, geprocess .h) getest (mics.h vga.h psx.h imagesup.h,geprocess.h) Далее следует текст файла getest.с:
Введение 407 / * * * * * * * * * * * * * * ф ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ ♦ / /* Демонстрационная программа ♦/ /♦ геометрических методов обработки ♦/ /♦ изображения ♦/ /♦ разработана в Turbo С 2.0 ♦/ /* Крэйгом А. Линдли */ /♦ ♦/ /♦ Версия: 1.0 ♦/ /* Последние изменения внесены 12/26/89 */ /♦ ф ф ф ф ф ф ф фф ф фф ф фф фф ф фф фф ф ♦ ♦ ♦♦ ♦ ♦♦ ♦ ♦♦ ♦♦ ♦ ♦♦ ♦/ ♦include <stdio.h> ♦include <conio.h> ♦include <dos.h> ♦include <alloc.h> ♦include <process.h> ♦include <graphics.h> ♦include “misc.h” ♦include “pcx.h” ♦include “vga.h” ♦include ”imagesup.h” ♦include "geprocess.h” /* Программа геометрической обработки */ void main(void) { char *InFileNamel e "p2cl2ila"; char *InFileName2 “p2cl2i2a"; char *InFileName3 s ”p2cl2i3a”; char *InFileName4 » ”p2cl2i4a”; char *InFileNameS » ”p2cl2i5a”; BYTE huge *lnlmage; BYTE huge *Buffer; unsigned GenPCXFiles ш FALSE; /* управляет созданием выходного файла */ InitGraphicsO; printf("Geometric Transofrm Example Program\n\n“); printf ("Reading the Image PCX File into memory\n“); /* Запрашиваем память для буфера изображения */ Buffer = (BYTE huge ♦) farcalloc(RASTERSIZE,(long)sizeof(BYTE)); if(Buffer == NULL) { restorecrtmodeO; printf ("Error Not enough memory for geometric operation\n"); exit(ENoMemory);
408 Глава 12. Геометрические процессы Листинг 12.2. (Продолжение) /* Приготовим последовательность изображений на рис. 12.2 - Увеличение изображения */ if(ReadPCXFileToBuf(InFileNamel.AInlmage) !“ NoError) exit(ENoMemory); DisplayImagelnBuf(InImage,INITVGALOADPALETTE,WAITFORKEY); /♦ Очищаем выходной буфер черным цветом •/ ClearImageArea(Buffer,MINCOLNUM,MINROWNUM,MAXCOLS.MAXROWS.WHITE); Scaleimage(InImage,119,13,83,111,1.3,1.3,Buffer,100,25,FALSE); DisplayImagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if (GenPCXFiles) Writ ePCXFile("p2c1Oilb",8,320,200,1,320) ; /• Очищаем выходной буфер черным цветом •/ ClearImageArea(Buffer.MINCOLNUM,MINROWNUM,MAXCOLS.MAXROWS,BLACK); Scaleimage(InImage,119,13,83,111,1.3,1.3.Buffer,100,25.TRUE); DisplayImagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if (GenPCXFiles) Writ ePCXFile("p2c1Oi1c",8,320,200,1,320); farfree((BYTE far •Hnlmage); /• освобождаем память ♦/ /* Приготовим последовательность изображений на рис. 12.3 - Уменьшение изображения ♦/ if(ReadPCXFileToBuf(InFileName2,&InImage) !ж NoError) exit(ENoMemory); DisplayImagelnBuf(InImage,NOVGAINIT,WAITFORKEY); /* Очищаем выходной буфер черным цветом */ ClearImageArea(Buffer,MINCOLNUM,MINROWNUM,MAXCOLS.MAXROWS.WHITE); Scaleimage(InImage,9,76,291,105,0.5,0.5,Buffer,80,74,FALSE); DisplayImagelnBuf(Buffer.NOVGAINIT,WAITFORKEY); if(GenPCXFiles) Writ ePCXFile("p2c10i2b",8,320,200,1,320); /♦ Очищаем выходной буфер черным цветом ♦/ ClearImageArea(Buffer.MINCOLNUM,MINROWNUM,MAXCOLS.MAXROWS.WHITE); Scaleimage(InImage,9,76,291,105,0.5,0.5,Buffer,80,74,TRUE); DisplayImagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) Writ ePCXFile("p2c10i2c",8,320,200,1,320); farfree((BYTE far *)lnlmage); /♦ освобождаем память */
Введение 409 /* Приготовим последовательность изображений на рис. 12.4 - Различные разнеры изображения */ if(ReadPCXFileToBuf(InFileName3.fclnlaage) !” NoError) exit(ENoMemory); DisplayImagelnBuf(InImage,NOVGAINIT,WAITFORKEY); /♦ Очищаем выходной буфер белым цветом, чтобы показать границу области, не затронутой процессом масштабирования. Это также создает приятную белую рамку в выходном изображении. */ ClearImageArea(Buffer,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS,WHITE); /* Изображение 1 увеличивается в 0.5 x 0.5 и занимает правый нижний угол */ Scaleimage(InImage.MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, 0.5,0.5,Buffer,155,95,TRUE); /♦ Изображение 2 увеличивается в 0.45 x 0.67 и занимает левый верхний угол */ Scaleimage(InImage,MINCOLNUM,MINROWNUM,MAXCOLS.MAXROWS, 0.45,0.67,Buffer,4,4,TRUE); /* Изображение 3 увеличивается в 0.5 x 0.42 и занимает правый верхний угол */ Scalelmage(InImage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, 0.5,0.42,Buffer,155,4,TRUE); /* Изображение 4 увеличивается в 0.25 x 0.25 и занимает левый нижний угол */ Scaleimage(InImage,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS, 0.25,0.25,Buffer,38,145,TRUE); DisplayImagelnBuf(Buffer.NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2c10i3b",8,320,200,1,320); farfree((BYTE far *)lnlmage); /* освобождаем память */ /* Приготовим последовательность изображений на рис. 12.5 - Вращение изображения ♦/ if(ReadPCXFileToBuf(InFileName4,ftInImage) != NoError) exit(ENoMemory); DisplaylmagelnBuf(InImage,NOVGAINIT,WAITFORKEY); /* Очищаем выходной буфер черным цветом ♦/ ClearImageArea(Buffer,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS,BLACK); Rotateimage(InImage,105,43,117,107,37.5,Buffer,FALSE); DisplaylmagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2c10i4b",8,320,200,1,320); ClearImageArea(Buffer,MINCOLNUM,MINROWNUM,MAXCOLS,MAXROWS,BLACK);
410 Глава 12. Геометрические процессы Листинг 12.2. (Продолжение) Rotateimage(InImage,105,43,117,107,37.5,Buffer,TRUE); DisplaylmagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2cl0i4c",8,320,200,1,320); ClearImageArea(Buffer,MINCOLNUH,MINROWNUM,MAXCOLS,MAXROWS.BLACK); Rotateimage(InImage,105,43,117,107,300.0,Buffer,FALSE); DisplaylmagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) WritePCXFile("p2cl0i4d",8,320,200,1,320); Clear lmageArea(Buf fer,MINCOLNUM.MINROWNUM.MAXCOLS .MAXROWS,BUCK); Rotateimage(InImage,105,43,117,107,300.0,Buffer,TRUE); DisplaylmagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) if(GenPCXFiles) WritePCXFile("p2c10i4e",8,320,200,1,320); farfree((BYTE far *)InImage); /* освобождаем память */ /* Приготовим последовательность изображений на рис. 12.6 - Зеркальные изображения */ if(ReadPCXFileToBuf(InFileName5,ftInImage) !ж NoError) exit(ENoMemory); DisplaylmagelnBuf(InImage,NOVGAINIT,WAITFORKEY); /* Очищаем выходной буфер черным цветом */ ClearImageArea(Buffer,MINCOLNUM.MINROWNUM.MAXCOLS,MAXROWS,BLACK); Translatelmage(InImage,82,76,166,48,Buffer,82,10,FALSE); Mirrorlmage(InImage,82,76,166,48,HorizMirror.Buffer,82,76); Mirrorlmage(InImage,82,76,166,48,VertMirror,Buff er,82,142); DisplaylmagelnBuf(Buffer,NOVGAINIT,WAITFORKEY); if(GenPCXFiles) Writ ePCXFile("p2c10i5b",8,320,200,1,320); farfree((BYTE far *)InImage); /* освобождаем память */ farfree((BYTE far *)Buffer); restorecrtmodeO; closegraph0; } Местоположение элементов изображения и интерполяция Применение геометрических процессов к изображению иногда приводит к тому, что теряется соответствие между исходным и выходным изображениями. В вы- ше рассмотренной обработке изображений можно было определить, как элемент
Местоположение элементов изображения и интерполяция 411 выходного изображения получается из исходного изображения, т. е. какие ис- ходные элементы были затронуты. Это точное соответствие или отображение всегда предполагалось. В геометрическом процессе не гарантируется точное соответствие исходных и выходных элементов изображения. Рассмотрим, например, масштабирование изображения. Если изображение увеличено, то один элемент в исходном изобра- жении может отображаться во многих элементах в выходном изображении. И наоборот, если исходное изображение уменьшено в размере, множество элемен- тов в исходном изображении может отображаться в одном элементе изображе- ния на выходе. Например, увеличим изображение в нецелое число раз 1,3. Как может один элемент исходного изображения точно соответствовать 1,3 элемен- тов в выходном изображении? Соответствие элементов исходного и выходного изображения в дальнейшем усложняется, так как оно осуществляется в перспективе выходного изобра- жения вместо перспективы исходного изображения. По-видимому, требуется и обратное отображение для гарантии того, что каждому элементу в выходном изображении задано значение. Без соответствия один к одному между исходны- ми и выходными элементами изображения нельзя гарантировать, что элемент исходного изображения будет отображен на каждом элементе выходного изобра- жения. Элементы выходного изображения, которым не задано значение, произ- водят шумы в выходном изображении. Как и ожидалось, эти шумы ухудшают качество полученного изображения. В действительности выходное изображение в общем случае непригодно. Для устранения шумов геометрические преобразования обычно применяют обратное отображение, которое выбирает элемент за элементом выходного изо- бражения и вычисляет путем преобразования, какие элементы исходного изобра- жения будут использованы при получении выходных элементов изображения. Если значение выходных элементов вычисляется таким образом и помещается в выходное изображение, то гарантируется полный охват выходного изображения. Обратное отображение элемента изображения создает другую проблему — дробный адрес элемента изображения. Это происходит, когда рассчитывается элемент исходного изображения, который задает значение выходному элементу. Недостаток отображения элементов изображения один к одному проявляется в вычисленном элементе исходного изображения, лежащем где-то между четырь- мя основными позициями в исходном изображении. Дробный адрес — это ма- тематическая функция, которая не имеет физического аналога. Реально адрес элемента изображения должен иметь целочисленное значение, которое соответ- ствует рядам и колонкам отображаемого изображения. При работе с дробными адресами применяется понятие интерполяции. Также существуют и другие методы присваивания целых адресов элементу изображе- ния по дробным адресам. Один из них в этой книге называется аппроксимацией к ближайшему соседу. Этот метод описан ниже в главе. А сейчас мы рассмотрим понятие линейной интерполяции. Процесс интерполяции, применяемый к положению элемента изображения — это тот же процесс интерполяции, который используется для вычисления зна- чений входов в логарифмических таблицах. Единственное отличие заключается в том, что точка интерполяции изображения, чтобы быть эффективной, должна
412 Глава 12. Геометрические процессы Исходное изображение Выходное изображение Рис. 12.1. Интерполяция значения элемента изображения. Примечания 1. Черный элемент на рис. б преобразуется с помощью геометрического процесса. Ре- зультатом преобразования является то, что элементу дается нецелый адрес, как пока- зано на рис. а. 2. Если не используется интерполяция, значение интенсивности черного элемента на рис. б дается элементу А на рис. а. 3. Если используется интерполяция, интенсивность преобразованных черных элементов получается от элементов А, В, С и D в соответствии с их относительным расстоянием от вычисленного адреса преобразованного элемента. Выполняется двумерная интерпо- ляция. быть определена в двух измерениях — горизонтальном и вертикальном. В обоих случаях интерполяция используется для расчета нового значения элемента изо- бражения, который располагается между другими элементами известного зна- чения и на известном расстоянии от элемента, который необходимо рассчитать. Элементы с известным значением, окружающие элемент, который необходимо рассчитать (преобразуемые элементы или «Рх»), называются областью примы- кания. Элементы в области примыкания, которые находятся ближе к Рх, имеют большее воздействие на значение вычисленного элемента. Элементы, располо- женные дальше, имеют меньше влияния. Линейная интерполяция допускает, что вклад элемента в области примыкания изменяется линейно с расстоянием. Необходимость интерполяции показана на рис. 12.1. Черный элемент Рх — это точка, подвергающаяся преобразованию. Обрат- ное отображение черного элемента помещает ее в исходное изображение (а) в дробный адрес, который находится где-то в области примыкания, определенной элементами А, В, С и D. Показано положение черного элемента в исходном изо- бражении в том случае, когда применялось вращение, хотя могут применяться почти все геометрические преобразования. Если используется аппроксимация к ближайшему соседу, то дробный адрес черного элемента на рис. а будет преобразован к ближайшему целому адресу элемента изображения. В этом случае выходное изображение будет задано зна-
Местоположение элементов изображения и интерполяция 413 чением элемента А, так как элемент А является ближайшим целым адресом. Подумайте над этим. Эта аппроксимация дает не очень хороший результат. На рис. 12.2 и 12.3 по- казан эффект обработанного с помощью этого приближения изображения. Обра- тите внимание на зубчатую природу преобразованных изображений. Наблюда- ется искажение в изображении. На том же тестовом изображении сравните этот результат с полученным при использовании интерполяции. На самом деле ин- терполированные изображения гораздо лучшего качества, хотя их производство более дорогое. При получении интерполированного значения для преобразования элемента в выходном изображении должны быть выполнены 3 интерполяции: вклад интен- сивности от АВ и CD. (Выполняются 3 интерполяции, так как мы интерполируем в двух размерах.) Как уже сказано выше, для правильной интерполяции должно быть известно расстояние от каждого элемента в области примыкания. На практике же долж- но быть известно только расстояние от элемента в верхнем левом углу области примыкания (элемент А на диаграмме) до преобразуемого элемента. В нашем обсуждении эти расстояния соответственно называются шагом в колонках и ря- дах. Другими словами, шаг колонки — это дробный адрес колонки элемента Рх минус целый адрес колонки элемента А. Шаг ряда определяется аналогично. С помощью алгебраических преобразований эта формула принимает вид: Шаг колонки (ColDelta) = дробный адрес колонки элемента Рх - целый адрес колонки элемента А. Шаг ряда (RovDelta) = дробный адрес ряда элемента Рх - целый адрес ряда элемента А. После того как рассмотрены величины, следуют 3 интерполяции: ContribAB = ColDelta*(интенсивность элемента В - интенсивность элемента А) + интенсивность элемента А ContribCD = ColDelta* (интенсивность элемента D - интенсивность элемента С) + интенсивность элемента С Поэтому Рх = RowDelta*(ContribCD - ContribAB) + ContribAB Значение, вычисленное для Рх и помещенное в выходное изображение, про- порционально интенсивностям и расстоянию Рх от каждого элемента в области примыкания. При использовании интерполяции в совокупности с обратным ото- бражением, результирующие геометрические преобразования изображения бу- дут очень высокого качества, т. е. они более четко отражают исходное изобра- жение после желаемого преобразования. Недостатком использования интерполяции является низкая производитель- ность. Расчеты, которые должны быть выполнены при использовании чисел с плавающей запятой, существенно замедляют геометрические процессы. Суще-
414 Глава 12, Геометрические процессы б Центр увеличен в 1,3 раза с интерполяцией Рис. 12.2. Увеличение изображения. ствует классическое соотношение между временем обработки, которое можно потратить, и требуемым количеством результирующего изображения. Так как это соотношение должно быть получено на основе «изображение от изображе- ния», функции, содержащиеся в библиотеке функций геометрических процессов, поддерживают как интерполяцию, так и аппроксимацию к ближайшему соседу. Выбор того или иного метода зависит от дальнейшего применения и имеющего- ся в наличии времени. В табл. 12.1 приведена дополнительная информация об использовании интерполяции.
Местоположение элементов изображения и интерполяция 415 Изображение уменьшено в 2 раза без интерполяции Изображение уменьшено в 2 раза с интерполяцией б в Рис. 12.3. Уменьшение изображения.
416 Глава 12. Геометрические процессы Таблица 12.1. Библиотека функций геометрических процессов Примечание Многие параметры функций геометрических процессов похожи или одинако- вы. Для того чтобы не повторяться, здесь приводятся следующие параметры: «InImage» — указатель на исходное изображение. Исходное изображение — это изображение, которое подвергается геометрическому преобразованию. Исход- ное изображение никогда не определяется геометрическим процессом, как выход- ное изображение («Outlmage»). «SCol», «SRow», «SWidth», «SHeight» — параметры, контролирующие часть исходного изображения, которое будет геометрически преобразовано в выходное изображение. «SWidth», «SHeight» — размер области изображения с положе- нием левого угла изображения в «SCol», «SRow». Эта область преобразуется с помощью геометрического процесса и результат помещается в выходное изобра- жение. «Outlmage» — указатель на выходное изображение. Выходное изображение (или его часть) преобразуется и помещается в буфер выходного изображения. Буфер выходного изображения всегда описывается с помощью геометрического процесса. «DCol», «DRow» — параметры, которые контролируют, где в выходном изображении будет помещено преобразованное исходное изображение (или его часть). Наряду с геометрической обработкой исходного изображения использова- ние этих параметров предусматривает перемещение. 1. Функция масштабирования изображения. Прототип void ScaleImage(BYTE huge *lnlmage, unsigned SCol, unsigned SRow, unsigned SWidth, unsigned SHeight, double ScaleV, BYTE huge *OutImage unsigned DCol, unsigned DRow, unsigned Interpolate); где все параметры определены, кроме: «ScaleH» и «ScaleV» — это факторы соответственно горизонтального и вер- тикального масштабирования. Они контролируют степень, в которой будет отмасштабировано исходное изображение (или его часть) в каждом из двух направлений. Если факторы масштабирования больше 1, то исходное изо- бражение должно увеличиваться, если факторы больше 0, но меньше 1, то изображение должно быть уменьшено в размере. (Смотрите более подробно описание соотношения геометрических размеров.) «Interpolate» — это логический параметр, который контролирует, нужно ли использование интерполяции или нет. Если он равен TRUE , то ин- терполяция будет использоваться, если он равен FALSE, то вместо этого будет использована аппроксимация к ближайшему соседу. Использование интерполяции занимает тогда очень много времени, но зато выходное изо- бражение всегда высокого качества (см. текст). Действие Функция масштабирования начинает свою работу с расчета того, на сколь- ко большим будет отмасштабированное изображение. Затем она проверя- ет, соответствует ли отмасштабированное изображение пределам буфера
Местоположение элементов изображения и интерполяция 417 выходного изображения. Если нет, то работа программы на этом закан- чивается. Если размер отмасштабированного изображения соответствует буферу выходного изображения, то продолжается функция масштабиро- вания. В этот момент вводятся 2 вложенных цикла, которые сканируют площадь выходного изображения (которая в в конечном счете будет зани- мать отмасштабированное исходное изображение) от элемента к элементу изображения. Для каждого элемента выходного изображения выполняется обратное преобразование для обнаружения адреса, который будет соответ- ствовать элементу выходного изображения в исходном изображении. Очень часто рассчитанный адрес элемента изображения будет находиться где-то между действительными адресами целых элементов изображения. Други- ми словами, рассчитанный адрес будет иметь дробные компоненты вместо целых. Затем идет ветвь программы функции масштабирования, основанная на том, будет ли использоваться интерполяция или нет. Если интерполя- ция не используется, адрес с дробными компонентами, рассчитанный для элементов выходного изображения в исходном изображении сокра- щается до адреса ближайших действительных элементов изображения и значения этого элемента в исходном изображении копируется в вы- ходное изображение. В тексте это называется аппроксимацией к бли- жайшему соседу. Если используется интерполяция, то выбираются все 4 элемента изображения, которые окружают рассчитанное положение элемента выходного изображения в исходном изображении. Значение, данное элементу выходного изображения, зависит от интенсивности и расстояния от каждого из четырех окружающих элементов изображе- ния. Линейная интерполяция используется для расчета влияния каждо- го из четырех окружающих элементов изображения на элемент, поме- щенный в выходное изображение. (Подробнее об интерполяции смотрите в тексте.) 2. Функция размера изображения. Прототип void SizeImage(BYTE huge *lnlmage, unsigned SCol, unsigned SRow, unsigned SWidth, unsigned SHeight, BYTE huge *OutImage, unsigned DCol, unsigned DRow, unsigned DWidth, unsigned DHeight, unsigned Interpolate); где все па- раметры определены, кроме: «DWidth» и «DHeight» — параметры, которые ограничивают площадь в выходном изображении, в которую будет помещаться отмасштабированное изображение. Действие Эта функция почти такая же, как и только что описанная функция «Scaleimage». Различие заключается в том, что вместо факторов масшта- бирования в горизонтальном и вертикальном направлениях, определяется область, в которой должно быть помещено отмасштабированное изобра- жение. Факторы масштабирования рассчитываются внутри этой функции, таким образом результирующее изображение будет соответствовать пред- писанной области. (Примечание: если соотношение ширины к высоте обла- сти выходного изображения отличается от этого соотношения в области исходного изображения, то формат отмасштабированного изображения бу- дет изменяться.) 27-3
418 Глава 12. Геометрические процессы Таблица 12.1. ^Продолжение) 3. Функция вращения изображения. Прототип void RotateImage(BYTE huge *lnlmage, unsigned SCol, unsigned SRow, unsig- ned SWidth, unsigned SHeight, double Angle, BYTE huge *OutImage, unsigned Interpolate); где все параметры определены, кроме «Angle» — это угол, на который будет повернуто исходное изображение против часовой стрелки в градусах. Действие Эта функция использует законы тригонометрии для вращения изображе- ния вокруг своей центральной точки на определенный угол. Уравнения, управляющие преобразованием ячейки элемента исходного изображения («Xold, Yold») в новую ячейку в выходное изображение («Xnew, Ynew»), записываются следующим образом: Хпеи e Xold ♦ Cos(Angle) + Yold ♦ Sin(Angle) Ynew » Yold ♦ Cos(Angle) + Xold ♦ Sin(Angle) Эта функция также действует от выходного изображения для того, чтобы предотвратить пустоты в выходном изображении. Во многих отношениях вращение изображения — это процесс, похожий на масштабирование изо- бражения. К сожалению, вращение изображения осложняется как рассмо- трением соотношения геометрических размеров, так и интерполяцией. 4. Функция сдвига изображения. Прототип void TranslateImage(BYTE huge *lnlmage, unsigned SCol, unsigned SRow, unsigned SWidth, unsigned SHeight, BYTE huge *OutImage, unsigned DCol, unsigned DRow, unsigned EraseFlag); где все параметры определены, кроме «EraseFlag» — это логический флаг, который определяет, должно ли быть очищено исходное изображение (или его часть), сдвинутое в ячейке с по- мощью этой функции (установка на черный). Если FALSE, то исходное изображение копируется в выходное изображение и исходное изображение остается нетронутым. Если TRUE, то пока изображение все еще копиру- ется, определенная часть исходного изображения очищается. Если буферы исходного и выходного изображения перекрываются, определение значения TRUE для параметра «EraseFlag» приводит к перемещению изображения или объекта внутри буфера. Действие Эта функция используется для копирования или перемещения изображе- ния (или объектов в изображении) в другой буфер изображения или в пре- делах одного буфера. Термин «translation» в компьютерной графике озна- чает сдвиг. Параметры, входящие в эту функцию, определяют не только часть исходного изображения, выбранную для сдвига, но и куда его пере- местить. Для пересечения исходного изображения на элемент изображения используются 2 вложенных цикла. В каждый отрезок времени элемент ис- ходного изображения перемещается в выходное изображение. Данное пре- образование осуществляется из исходного вместо выходного изображения.
Соотношение геометрических размеров 419 Это противоречит всем другим геометрическим процессам. Преобразова- ние может быть выполнено этим способом, так как между исходными и выходными элементами изображения существует соответствие один к одно- му, что устраняет пустоты в выходном изображении. Как уже говорилось, если функция «EraseFlag» установлена в TRUE, то исходное изображение (или его определенная область) должна быть очищена в черный цвет. 5. Функция зеркального отражения изображения. Прототип void MirrorImage(BYTE huge *lnlmage, unsigned SCol, unsigned SRow, un- signed SWidth, unsigned SHeight, enum MirrorType WhichMirror, BYTE huge *OutImage, unsigned DCol, unsigned DRow); где все параметры определены, кроме: «WhichMirror» — это параметр перечисленных типов, который определяет, какой тип зеркальной функции осуществляется. Существуют два типа зер- кальной функции «HorizMirror» и «VertMirror». «HorizMirror» создает зер- кальное отражение исходного изображения в горизонтальном направлении, т. е. значение исходного изображения слева становится выходным изобра- жением справа, а значение исходного изображения справа становится вы- ходным изображением слева. «VertMirror» создает зеркальное отражение в вертикальном направлении. Верхушка исходного изображения становится нижней частью выходного изображения, и наоборот. Действие Эта функция очень просто размещает снова элементы входного изображе- ния для получения выходного изображения. Никаких сложностей при этом не наблюдается из-за соответствия один к одному элементов исходного и выходного изображения (дополнительная информация содержится в ли- стинге 12.1). Как и в пространственных процессах, описанных в гл. 10, в интерполяции не- обходимо учитывать «граничные эффекты» при выполнении алгоритма. В дей- ствительности в любом алгоритме обработки изображения, который использует понятие области примыкания элемента изображения, возникают проблемы, ко- гда необходимо обрабатывать края изображений. Решение, используемое для интерполяции, отличается от решений, исполь- зуемых в пространственных процессах. Данные элементы изображения области примыкания создаются в краях изображения. В пространственных процессах края не подлежат обработке (см. листинг 12.1). Интерполяция важна, когда изображения масштабируются, измеряются и/или подвергаются вращению. Все остальные процессы, включенные в библио- теку функций геометрических процессов, сохраняют отображение один к одному и поэтому не требуют использования интерполяции. Соотношение геометрических размеров В нашем случае формат изображения может быть определен как пропорцио- нальное соотношение высоты к ширине изображения, показанного на мониторе. 27*
420 Глава 12, Геометрические процессы Окружность, показанная в правильном формате изображения, имеет круглую форму. Неудачный формат искажает окружность таким образом, что она бу- дет выглядеть как эллипс. Искажения в соотношениях геометрических размеров обычно являются результатом вывода изображения при использовании так на- зываемых «неквадратных элементов изображения» или при попытках показать изображение с нарушенным соотношением геометрических размеров исходного изображения. Последние искажения можно легко устранить. Более подробная информация об этом приведена в главе ниже. С неквадратными элементами го- раздо труднее работать. Для выяснения важности соотношения геометрических размеров рассмотрим сначала известный формат изображения типичных видеоприборов (телевизоров, компьютерных мониторов и камер), который равен 4:3. Это означает, что шири- на экрана монитора приблизительно составляет 1,333 высоты экрана. Когда на монитор выводится прямое изображение, формат изображения будет правиль- ным, так как оба прибора имеют соотношение 4:3. Таким образом, наблюдаемый с помощью комбинации камера/монитор круглый объект будет круглым, что и следовало ожидать. Режим VGA 640 х 480 имеет квадратные элементы, так как соотношение ши- рины и высоты подобранного элемента такое же, как и у стандартного видео (640/480 равно точно 4/3). Другими словами, перемещение одного элемента в горизонтальном направлении отражает то же расстояние в изображении, что и перемещение одного элемента в вертикальном направлении. Рассмотрим в каче- стве примера монитор IBM 8512. Ширина его экрана приблизительно равна 9,5 дюйм, в то время как высота приблизительно равна 7,125 дюйм, т. е. идеаль- ное соотношение 4:3. Для определения разрешения дисплея в дюймах выполним следующие расчеты: PixelsPerlnchHoriz • 640 pixels/9,5 inches • 67,37 pixels/inch PixelsPerlnchVert 480 pixels/7,125 inches « 67,37 pixels/inch Как видно, расстояние в дюймах, представленное смещением единичной точки в горизонтальном и в вертикальном направлениях, одинаково. (Обычно приня- тое значение разрешения компьютерного монитора равно 71 точке или элементу изображения/дюйм. Как вы видите, рассчитанные нами значения очень близ- ки.) Кроме того, показанный элемент может быть квадратным, как следует из приведенных ниже вычислений: InchesPerPixelHoriz « 1/PixelsPerInchHoriz » .0015 inches InchesPerPixelVert 1/PixelsPerInchVert » .0015 inches Таким образом, каждый элемент на экране размером 640 х 480 приблизительно равен 0,0015 х 0,0015 кв. дюйм. К сожалению, видеорежим с разрешением 320 х 200, используемый в книге во всех обработках изображения, имеет искажения, обусловленные неквадратны- ми элементами: демонстрируемые элементы оказываются более высокими, чем широкими. Повторяя предыдущий расчет, получаем PixelsPerlnchHoriz • 320 pixels/9,5 inches « 33,68 pixels/inch PixelsPerlnchVert • 200 pixels/7,125 inches • 28,07 pixels/inch
Соотношение геометрических размеров 421 Поэтому InchesPerPixelHoriz = 1/PixelsPerInchHoriz = .0296 inches InchesPerPixelVert s 1/PixelsPerInchVert « .0356 inches Таким образом, каждый элемент на экране размером 320 х 200 приблизительно имеет ширину 0,0296 дюйм и длину 0,0356 дюйм, т. е. является неквадратным. Такой же расчет можно выполнить для режима с разрешением 640 х 200. В результате получим, что каждый элемент имеет ширину, равную половине ши- рины элемента при разрешении 320 х 200, но той же высоты, т. е. снова получаем неквадратные элементы. При применении к изображению геометрического процесса вращения стано- вится важным формат изображения, если имеются неквадратные элементы (т. е. используется разрешение 640 х 480). Вращение изображения является обычной операцией, так как формат изображения и развернутое изображение не искажа- ются в процессе вращения. Требуется дополнительный расчет для сохранения формата изображения объекта в процессе вращения. Эти дополнительные рас- четы замедляют выполнение вращения изображения, но улучшают результат. При использовании поправки формата изображения и интерполяции получают- ся самые лучшие возможные развернутые изображения. Соотношение геометрических размеров объектов в процессе вращения оста- ется постоянным благодаря преобразованию смещения всех элементов от цен- тра изображения (центра вращения изображения) к элементам, подвергающимся вращению, в абсолютное расстояние в дюймах. При этом номер колонки элемен- та умножается на значение «InchesPerPixel Horiz», чтобы получить расстояние на экране дисплея в дюймах по горизонтали. Затем эти абсолютные значения размера используются в расчетах превращений. Наконец, числа преобразован- ных размеров (которые представляют абсолютное расстояние от центра изобра- жения до положений новых элементов) преобразуются обратно в координаты элементов экрана. Так как абсолютные значения расстояния от центра изобра- жения сохраняются для каждого развернутого элемента, формат изображения остается прежним. Этот процесс описывается в листинге 12.1 в программе функ- ции вращения. Обсуждение соотношения геометрических размеров также важно при исполь- зовании цифрового преобразователя, описанного в ч. I книги. Очевидно, что в режиме 320 х 200 или 640 х 200 формат изображения будет неверным. Только изображения с высоким разрешением 640 х 480 будут иметь то же соотношение геометрических размеров, что и сам предмет, подвергшийся оцифровке и затем выведенный на экран. Пространственная аберрация, вызванная неправильным соотношением геометрических размеров изображения в режимах низкого разре- шения дисплея, не учитывалась до этого времени. Это было возможно, так как формат изображения существенно не влиял на внешний вид цифрового изобра- жения и в этой главе не представлены методы коррекции этих погрешностей. Рас- смотрение коррекции формата изображения ранее вносило бы ненужную слож- ность. Проблема стала очевидной только тогда, когда оцифровывался большой круглый предмет. Как и предполагалось, окружность была больше похожа на эллипс.
422 Глава 12. Геометрические процессы Теперь у нас есть методы, необходимые для коррекции разрешения дисплея 320 х 200. Этими методами являются интерполяция и коррекция соотношения геометрических размеров изображения. Необходимо отметить, что добавляя про- граммы коррекции соотношения геометрических размеров к процедуре получе- ния изображения, мы будем получать более качественные изображения, но для этого потребуется дополнительное время. Соответствие коррекции соотношения геометрических размеров изображения зависит от применения. Фрагмент про- граммы «acquire.с», показанный в листинге 12.3, иллюстрирует, как коррекция соотношения геометрических размеров изображения может быть включена в основной алгоритм ввода изображения. Данная программа (наряду с видеопре- образователем, описанным в ч. I книги) оцифровывает и произвольно записывает изображения с разрешением 320 х 200, которые имеют коррекцию соотношения геометрических размеров. Программа «асquire.с» используется для расчета всех изображений, описанных в ч. И. Программа получения изображения требует две модификации для включения коррекции соотношения геометрических размеров изображения с разрешением 320 х 200. а) Вместо получения изображений 320 х 200 элементов с помощью видеопре- образователя, получаем изображение 320 х 240. (Примечание: 320 х 240 — это соотношение геометрических размеров 4:3.) б) Программа вывода на экран цифрового изображения, полученного из большого изображения, видоизменяется так, чтобы демонстрировать 240 линий видеоинформации, используя 200 вертикальных элементов. Други- ми словами, значения 240 линий располагаются среди 200 возможных вер- тикальных элементов изображения. Интерполяция используется для точ- ного расчета значений интенсивности 200 вертикальных элементов. Эф- фект сжатия 240 линий от оцифрованного видеоизображения в 200 эле- ментов дисплея проявляется в компенсации неквадратных элементов. По существу, мы синтезируем квадратные элементы изображения. Листинг 12.3. Фрагмент программы коррекции соотношения геометрических раз- меров Как уже говорилось выше, элементы в изображении с разрешением 320 х 200 явля- ются неквадратными. Это проявляется в соотношении геометрических размеров изо- бражения, когда отображаются требуемые изображения. Для коррекции соотношения геометрических размеров изображения 4:3, производящей квадратные элементы, раз- решение расширяется до 320 х 240. Квадратные элементы можно аппроксимировать, ис- пользуя имеющиеся 240 линий оцифрованного видеоизображения и отображая 240 ли- ний видеоинформации в 200 вертикальных элементов дисплея, т. е. сжимая 240 линий видеоизображения в 200 линий. Это осуществляется с помощью интерполяции значе- ний элементов непосредственно во время процесса ввода изображения. Полный процесс описывается ниже. Поскольку видеопреобразователю требуется изображение с разре- шением 320 х 240, то необходим большой буфер изображения. Разрешение 320 х 200 требует 64000 байт памяти, а разрешение 320 х 240 требует 76800 байт. Переменная «RasterSize» должна быть установлена следующим образом: RasterSize = 76800L ; /достаточно для изображений 320x240/
Соотношение геометрических размеров 423 Затем должно быть определено, что видеопреобразователь требует большого изо- бражения. Это осуществляется с помощью изменений в структуре «ImageReq»: Reg.HMode ж LovRes; Reg•VMode • NonInterlace; Reg.FirstLine * 0; Reg.FirstPixel » 0; Reg.LastLine ж 240; /♦ определяет изображение 320x240 ♦/ Reg.LastPixel * 320; Теперь видеопреобразователь полностью подготовлен к вводу большого изображе- ния. Так как получено изображение с разрешением 320 х 240, то после выполнения коррекции соотношения геометрических размеров результат представлен в функции, которая выводит на экран полученные данные. Определение, показанное ниже, — это фактор превращения, который вызывает сканирование буфера видеоизображения со скоростью 1,2 видеоряда к рядам дисплея вместо исходных 1:1. Коэффициент 1,2 по- лучен делением 240 линий видеоизображения на 200 вертикальных элементов изобра- жения, достижимых на дисплее. tdefine RovAspectCorrection (double) 1,2 tdefine NumberOfVideoLines 240 Наконец, программа демонстрации оцифрованного видеоизображения должна быть из- менена в соответствии с соотношением геометрических размеров изображения. Заме- чания в программе, описанной ниже, показывают, как это сделать. /♦ Вывод оцифрованного изображения в VGA-режиме 13Н с 64 уровнями яркости. Коррекция изображения с соотношением геометрических размеров $320\times 200$. ♦/ void DisplayPictData (char huge ♦PictData) { register unsigned Col, Rov, Color, LoverColor, UpperColor; unsigned LoverBufferRov, UpperBufferRov; unsigned long PixelBufOffset; double FractionalRovAddr, RovDelta; /♦ Для каждой колонки дисплея. Всего 320. ♦/ for (Col=0; Col < LRMAXCOLS; Rov++) { /♦ Расчет начала оцифрованной видеоинформации в буфере изображения для этой строки. ♦/ PixelBufOffset = (long) NumberOfVideoLines ♦ Col; /♦ Для каждой из 200 строк, имеющихся в этом режиме дисплея... ♦/ for(Rov=0; Rov < LRMAXROWS; Rov++); { /♦ К какой видеостроке из 240 мы обращаемся? Вычисленный адрес будет находиться между двумя действительными адресами. Адрес будет дробным. ♦/
424 Глава 12. Геометрические процессы Листинг 12.3. (Продолжение) FractionalRowAddr s RowAspectCorrection ♦ (double) Row; /♦ Получить адрес строки байтов выше и ниже рассчитываеного дробного адреса. Выбрать значения интенсивности каждого. */ LowerBufferRow s (unsigned) FractionalRowAddr; UpperBufferRow = LowerBufferRow + 1; LowerColor = PictData[PixelBufOffset + LowerBufferRow]; UpperColor » PictData[PixelBufOffset + UpperBufferRow]; /* Расчет расстояния дробного адреса от нижнего действительного адреса. Это расстояние требуется для процесса интерполяции. ♦ / RowDelta = FractionalRowAddr - LowerBufferRow; /* Интерполяция значения интенсивности отмеченного элемента этой строки. * / Color s RowDelta*((double) UpperColor - LowerColor) + LowerColor; /♦ Отображение рассчитанной интенсивности на экране. * / PutPixel256(Col,Row,Color); Если PCX-файл создается для демонстрируемого изображения, данные, записанные в PCX-файл, читаются с экрана монитора (видеопамяти). По этой причине любое де- монстрируемое изображение с коррекцией соотношения геометрических размеров так- же записывается с коррекцией. Такой же метод можно применять и для коррекции соотношения геометри- ческих размеров изображений с разрешением 640 х 480 (вместо 640 х 200) и затем отобразить информацию, содержащуюся в 480 линиях видеоинформации в 200 вертикальных элементов дисплея. Это такой же процесс, как и только что опи- санный. Его осуществление мы оставляем читателям. Масштабирование изображения и изменение его размеров Масштабирование — геометрический процесс, который позволяет изменять раз- мер части изображения или всего изображения. Результирующее изображение будет увеличенным или уменьшенным вариантом исходного изображения. Це- лью масштабирования является получение преобразованного изображения с воз- можно наименьшей пространственной аберрацией. Описанный выше метод ин- терполяции помогает минимизировать аберрацию промасштабированного изобра- жения.
Масштабирование изображения и изменение его размеров 425 В библиотеку геометрических функций включены 2 функции изменения раз- мера изображения: «Scalelmage» и «Sizeimage». Обе они функционируют одина- ково. Функция «Scalelmage» определяет характеристики для масштабирования в горизонтальном и вертикальном направлениях. Функция «Sizeimage» вычисля- ет факторы горизонтальной и вертикальной шкалы масштаба, требуемые для того, чтобы изображение соответствовало определенной площади. Обе функции изменяют соотношение геометрических размеров промасштабированцого изобра- жения, если факторы шкалы в обоих направлениях неравны. Необходимость модификаций в соотношении геометрических размеров изображения зависит от приложения. При использовании функций геометрических преобразований важ- но помнить о соотношении геометрических размеров. На рис. 12.2-12.4 пока- заны различные операции масштабирования. Подробно эти функции описаны в табл. 12.1. Процессы масштабирования представлены с точки зрения выходного изобра- жения, т. е. для каждого элемента в выходном изображении представлено обрат- ное преобразование, для того чтобы рассчитать, какой элемент или элементы изображения в исходном изображении соответствуют элементам в выходном изо- бражении. Использование обратного преобразования гарантирует, что каждый элемент в выходном изображении будет иметь определенное значение. Если обратное преобразование не используется и элементы выходного изображения рассчитываются из исходного изображения, то в выходном изображении могут появиться пустоты. Это происходит оттого, что нет необходимого соответствия один к одному между элементами исходного и выходного изображения, когда масштабирование изображения осуществляется с помощью произвольных зна- чений. Обратное преобразование будет также автоматически выполнять повто- рение элементов изображения, как требовалось при увеличении изображения. Основная сложность масштабирования изображения и его оценки заключа- ется в использовании интерполяции для расчета преобразованных значений ин- тенсивности элементов изображения. Отметим, что геометрический процесс масштабирования может изменять про- странственное расположение данных изображения до такой степени, что данные исходного изображения не восстанавливаются, т. е. пространственная инфор- мация будет потеряна. Уменьшение размера изображения иллюстрирует такую потерю. Так как изображение уменьшено в размере, один элемент выходного (уменьшенного) изображения может соответствовать многим элементам выход- ного (увеличенного) изображения. Если изображение увеличено на ту же вели- чину, на какую было прошлый раз уменьшено, то в процессе уменьшения изо- бражения 4 элемента изображения станут одним и, следовательно, в процессе увеличения один элемент изображения будет отображен 4 раза. Очевидно, что в этих двух процессах преобразования информация потеряется. Ограничение функции масштабирования, предусмотренной библиотекой функций геометрических процессов, является необходимость масштабированно- го изображения полностью находиться в буфере изображения. Если обнаружит- ся, что преобразованное изображение превышает размеры буфера, то обе функ- ции масштабирования прекратят свое выполнение. Это происходит, конечно, ко- гда изображение увеличивается, а не уменьшается в размере. Для того чтобы это исправить, необходимо уменьшить площадь части изображения, которая подвер-
Исходное изображение б Рис. 12.4. Различные размеры изображения.
Вращение 427 гается увеличению, таким образом, увеличенное изображение будет полностью соответствовать ограничениям буфера изображения. Масштабирование изображения, как уже говорилось, имеет много примене- ний. Достаточно сказать, что процесс геометрических преобразований полезен сам по себе или в сочетании с другими процессами обработки изображения. На рис. 12.4 показано изображение в различных масштабах. Вращение Геометрический процесс вращения позволяет поворачивать изображение вокруг точки центра на любой произвольный угол. Вращение изображения осуществля- ется с помощью отдельного вращения каждого элемента, которые составляют изображение. Уравнения, которые управляют перемещениями позиций элемен- тов исходного изображения («Xold», Yold») в их новые позиции в выходном изо- бражении («Xnew, Ynew») имеют следующий вид: Xnew » Xold ♦ Cos (Angle) + Yold ♦ Sin (Angle) Ynew = Yold ♦ Cos (Angle) - Xold ♦ Sin (Angle) Эти уравнения и их доказательства имеются во многих учебниках по элемен- тарной тригонометрии или в книгах по компьютерной графике в разделе враще- ния векторов. Если вам необходимы доказательства, то обратитесь к одной из этих книг. Как уже говорилось, когда осуществляется вращение изображения, очень важно учесть соотношение геометрических размеров изображения. Вращение изображения превращается почти в тривиальный процесс при работе с квадрат- ными элементами изображения на дисплее. Неквадратные элементы изображе- ния значительно усложняют вращение изображения благодаря воздействию на соотношение геометрических размеров. К сожалению, режим 320 х 200, в котором проводится вся обработка изображения в этой книге, страдает от неквадратных элементов изображения. Для более детальной информации по этому вопросу смотрите описание соотношения геометрических размеров изображения. При вращении изображения с разрешением 320 х 200 без коррекции соотноше- ния геометрических размеров получается изображение, не находящее никакого применения. По этой причине коррекция соотношения геометрических размеров изображения встраивается в программу библиотеки функций геометрических процессов и не могут быть выключены из нее. Использование соотношения гео- метрических размеров наряду с интерполяцией дает очень точное соответствие исходного изображения независимо от угла, на который оно поворачивается. На рис. 12.5 показано повернутое изображение с интерполяцией и без нее. Увеличе- ние, выполненное с помощью интерполяции, является безошибочным. Все изо- бражения, показанные на этом тестовом изображении, используют коррекцию соотношения геометрических размеров изображения. Сдвиг Сдвиг — геометрическое преобразование, которое позволяет изображению или его части изменять свое положение. При обработке изображения и в компью- терной графике сдвиг означает перемещение. Перемещение изображения может
428 Глава 12, Геометрические процессы Исходное изобоажение В Рис. 12.5. Вращение изображения.
Сдвиг 429 Исходное изображение а Рис. 12.6. Зеркальное изображение. Зеркальное изображение Вверху - исходное изображение В середине - горизонтальное отражение Внизу - вертикальное отражение осуществляться либо между буферами изображений, либо внутри буфера изо- бражения. Данное преобразование осуществляется от исходного изображения, а не от выходного. Это противоречит всем описанным выше геометрическим процессам. Сдвиг выполняется таким путем, так как отображенные один к одному исход- ные и выходные элементы изображения устраняют возникновение пустот в вы- ходном изображении. На это соответствие не влияют искажения соотношения геометрических размеров и при этом не нужно использовать интерполяцию, так как сдвиг не нарушает его. Благодаря этому сдвиг является относительно бы-
430 Глава 12. Геометрические процессы стрым процессом по сравнению с масштабированием изображения и/или враще- нием. Программа функции «Translatelmage» показана в листинге 12.1. Изучите этот листинг для большего понимания работы этой функции. Так как сдвиг изобра- жения продемонстрирован во многих тестовых изображениях книги, специаль- ных тестовых изображений для сдвига мы не приводим. Зеркальные изображения Зеркальное изображение просто размещает снова элементы в исходном изобра- жении (или его части) для получения зеркального исходного изображения как выходного. Результирующее преобразование изображения выглядит так, как если бы было использовано зеркало для его образования. В библиотеке функций геометрического процесса содержатся два типа зеркальных изображений: гори- зонтальная зеркальность, которая образует зеркало исходного изображения в горизонтальном направлении, и вертикальная зеркальность, которая функцио- нирует в вертикальном направлении. При горизонтальной зеркальности левая часть исходного изображения становится правой частью выходного изображе- ния, а правая часть исходного изображения становится левой частью выходного изображения. При вертикальной зеркальности верхушка исходного изображе- ния становится низом выходного изображения, и наоборот. При использовании зеркального изображения не возникает никаких трудно- стей, так как имеется соответствие один к одному между исходными и выходны- ми элементами изображения. По этой причине не учитывается ни соотношение геометрических размеров, ни интерполяция. Программа функции «Mirrorimage» показана в листинге 12.1. Обратите, по- жалуйста, внимание на детали этой функции в листинге. На рис. 12.6 показана как горизонтальная зеркальность, так и вертикальная зеркальность примени- тельно к изображению. Заключение Выше мы рассмотрели основные геометрические процессы, которые можно при- менять к данным изображения. Как и другие процессы обработки изображения, геометрические процессы имеют ограничения, которые могут быть сведены до минимума и/или устранены путем использования коррекции соотношения геоме- трических размеров. Приведено много тестовых изображений для иллюстрации действия геометрических процессов. Кроме программы, которая корректирует соотношение геометрических раз- меров изображения с разрешением 320 х 200, представлены оцифрованные изо- бражения. Используя эти методы, можно получать изображения с разрешением 320 х 200 без аберраций соотношения геометрических размеров изображения.
Часть III Дополнительные сведения Толковый словарь терминов Амплитудное разрешение (Amplitude resolution). Точность, с которой АЦП преобразует аналоговый сигнал в цифровой. Чем больше число бит, ис- пользуемых для преобразования, тем выше разрешение. Анимация (Animation). Создание движущейся картинки с помощью компью- тера. Быстрая смена изображения на графическом устройстве создает эффект движения. АРА (All Points Addressable). Все точки адресуемы. Это выражение исполь- зуется применительно к растровым дисплеям и принтерам. Указывает на то, что каждый элемент устройства управляется независимо. Аргумент. Данные, передаваемые функции или программе. Аргументы или переключатели командной строки. Аргументы, передава- емые программе при запуске из командной строки DOS; строка аргументов, которая следует за командой DOS, включая команду запуска прикладной программы. АЦП. Аналого-цифровой преобразователь. Устройство, которое анализи- рует аналоговый сигнал и преобразует его в пропорциональное представление в цифровом виде. Байтовый массив. Тип изображения, в котором каждый элемент изображения занимает один байт (восемь бит). Один байт может содержать до 256 уровней серого цвета. Цветное изображение, использующее палитру, также может счи- таться байтовым массивом. Бинарное изображение. Изображение, которое использует только один бит для каждого элемента. Бинарное изображение выводится как черно-белое. Если элемент изображения равен 1, он — белый, а если 0 — черный. BIOS. Набор функций, который расположен в ПЗУ и контролирует ресурсы компьютерных систем, совместимых с IBM. Сокращение термина Basic Input Output System (базовая система ввода-вывода). Битовый массив. Отображение в памяти части изображения. Размер битового массива зависит от разрешения дисплея и количества поддерживаемых цве- тов.
432 Толковый словарь терминов Вектор. Направленный отрезок прямой линии. Вертикальный обратный ход. Временной интервал между отображением раз- личных полукадров. За это время электронный луч возвращается из правого нижнего угла ЭЛТ в левый верхний угол. При этом электронный пучок от- ключается. Вертикальный обратный ход возникает примерно 50 раз в 1 с в зависимости от ЭЛТ. Включаемые файлы. Файлы, которые включаются в исходный код на этапе компиляции. Обычно содержат идентификаторы, на которые имеются ссылки в исходном файле. Применяются для унификации использования идентифи- каторов в прикладной программе. Горизонтальный обратный ход (horizontal retrace). Временной интервал, за время которого электронный луч возвращается с правой границы экрана на левую. При этом электронный пучок отключается. Графический адаптер. Электронная схема, разработанная для обеспечения взаимодействия дисплея (монитора) и компьютера. Обычно (но не всегда) раз- мещается на отдельной плате. Графический режим. Режим графического адаптера, при котором все точки управляются независимо. Он отличается от текстового режима, при котором на экран можно вывести только заранее определенные символы. Двухуровневое изображение. Синоним бинарного изображения. Динамический диапазон. Соотношение между самым светлым и самым тем- ным уровнем интенсивности. Защелка. Тип устройства памяти, которое может принимать и хранить несколь- ко бит цифровых данных. Изображение. Физическое подобие; двумерное представление документа (фо- тографии, рисунка, текста и т. д.) или естественного объекта в виде таблицы чисел; двумерная функция, которая получена с помощью приемного устрой- ства и записывает значения характеристик изображения для всех его точек. Эти значения могут быть двоичными для черно-белых изображений, а так- же представлять собой уровни серого цвета для полутоновых изображений. Изображения преобразуются в цифровую форму для обработки с помощью компьютера. Интерполяция. Определение значения точки, которая находится между из- вестными точками на желаемом пути или контуре, задаваемом математиче- ской функцией. Картинка. Набор элементов, которые при одновременном отображении образу- ют воспринимаемое изображение. Квантование. Процесс трансформации значений яркости или цвета в целочи- сленные значения. Процесс квантования выполняется АЦП. Линия сканирования. Одна строка отображаемых данных. Ловушка кадра. Устройство ввода, которое получает изображение от видео- камеры и последовательно его оцифровывает в определенное число битов на элемент. Ловушка экрана обычно оцифровывает оба полукадра видеокадра для получения более высокого разрешения, а ловушка полукадра оцифровы- вает только один из полукадров, составляющих видеокадр. Маска. Метод удаления некоторых бит из набора (оставляются только нужные биты).
Толковый словарь терминов 433 Масштабирование. Преобразование координат элементов изображения для манипулирования размером изображения выполняется путем умножения ко- ординат элемента на фактор масштабирования, который может отличаться для направлений х и у. Матрицирование. Имитация серых областей с помощью фиксированного набо- ра матриц, каждая из которых представляет один уровень серого цвета. Машинно-зависимый код. Код, который зависит от некоторых характеристик конкретного компьютера. Машинно-независимый код. Код, который может выполняться на любом компьютере, поскольку он не зависит от конкретных особенностей какого- либо компьютера. Модель памяти. Обозначение, которое сообщает компилятору, где размещать код и данные программы, а также как на них ссылаться. Монитор. Синоним ЭЛТ или дисплея. Монохромное изображение. Изображения, которые отображаются с помо- щью уровней серого цвета; одноцветные изображения. Насыщенность. Определяет чистоту цвета или то, насколько он разбавлен бе- лым цветом. Красный цвет является очень насыщенным цветом. Розовый цвет имеет ту же цветность, но более низкую насыщенность. NTSC (National Television Standard Committee). Видеостандарт, распро- страненный в Северной и Центральной Америке и Японии. Этот стандарт определяет чересстрочное изображение с 525 линиями и временем сканиро- вания 1/30 с. Обрезать. Удалить часть изображения за пределами определенной границы. Отношение сигнал/шум. Отношение между уровнем сигнала (или существен- ной информации) и уровнем шума. PAL. Другой видеостандарт, распространенный в Европе, Австралии и Новой Зеландии. Содержит 625 линий с частотой 1/25 с. Палитра. Набор цветов, одновременно доступных для отображения на экране. Pel (Picture element). Наименьшая часть устройства вывода изображений, ко- торая может быть адресована, а также может переходить из видимого в не- видимое состояние. Пиксел. Элемент изображения на логическом и концептуальном уровне. Полутонирование. Процесс преобразования полутонового изображения в би- нарное (черно-белое) изображение для отображения или печати. Различные уровни серого цвета моделируются подбором размера черной точки внутри белого элемента изображения. Полутоновые изображения. Изображения, которые содержат непрерывный ряд интенсивностей от черного цвета до белого. Пороговая отсечка. Простейший метод преобразования полутонового изобра- жения в бинарное. Этот процесс выполняется путем сравнения значения ка- ждого элемента изображения с константой, называемой пороговым значением, или порогом. Элементы, значения которых превышают порог, будут белыми, а остальные — черными. Результатом является высококонтрастное, черно- белое изображение. По умолчанию (default). Значение, которое существует, если не определено никакое другое значение. 28-3
434 Толковый словарь терминов Приведение или приведение типа. Метод принудительного преобразования переменных от одного типа к другому. Псевдотонирование (dithering). Метод отображения полутоновых изображе- ний на монохромном мониторе путем тщательного подбора белых и черных элементов на экране. Разрешение. Число различимых точек в данном поле наблюдения. Плотность точек определяет число видимых деталей изображения. Чем больше деталей изображения, тем выше разрешение. Растр. Предопределенная структура линий, которая обеспечивает однородное покрытие пространства дисплея. RGB. Цветовая модель, которая образует цвета путем смешивания различных частей красного, зеленого и синего. Светимость. Яркостная составляющая видеосигнала. Связанное разрешение. В квантовании — плотность элементов отбора. SECAM. Видеостандарт, используемый в Иране, Польше, России и Саудовской Аравии. Поддерживает 625 линий с частотой 1/25 с. (Во Франции 819 линий.) Сжатие. Различные методы, используемые графическими устройствами и про- граммами для уменьшения размеров хранимых изображений без существен- ного ухудшения качества изображения. Сканер. Устройство, способное получать данные об изображении. Сканер пре- образует полутоновую картинку в цифровую форму. Соотношение геометрических размеров (Aspect ratio). Отношение шири- ны отдельного элемента изображения на экране к его высоте. Если дисплей имеет соотношение геометрических размеров, равное 1, это означает, что он имеет квадратные элементы изображения. CCD-камера. Видеокамера на ПЗС (прибор с зарядовой связью). Такие камеры меньше по размерам и более удобны, чем старые видеокамеры. CCITT (International Telegraph and Telephone Consultative Committee). Международная организация, которая разрабатывает и распространяет стан- дарты в промышленности средств связи. Точка (dot). Наименьший управляемый элемент на устройстве типа АРА. Ча- сто называется пискел (pixel) или пел (pel). Трансформация. Математическое преобразование одного набора чисел в дру- гой, основанное на некоторой трансформации. Уровень серого цвета. Когда аналоговый видеосигнал оцифровывается, по- лученные числовые значения должны быть промасштабированы в диапазоне целых чисел (представляющих собой уровни серого цвета) для дисплея. По- скольку полутоновые изображения содержат информацию о тонах серого цве- та, для них требуется более одного бита на элемент изображения. Обычно ис- пользуют один байт на элемент. Число доступных уровней серого цвета связа- но с амплитудным разрешением цифрового преобразователя и характеристи- ками имеющегося дисплея. Для реалистического отображения монохромных изображений достаточно иметь от 64 до 256 уровней серого цвета. Факсимиле или факс. Система передачи текстовых или графических изобра- жений. Изображение оцифровывается в источнике, передается через некото- рую среду, воссоздается в приемнике и затем печатается.
Толковый словарь терминов 435 Фильтр. Устройство, метод или программа, которая разделяет данные, сигналы или материалы на основе определенных критериев. Цветность. Имя цвета. Цветность цвета представляет собой его основную длину волны. Цветовая составляющая видеосигнала. Цветной фильтр. Полоска цветного стекла или пластика, используемая в фо- тографии для поглощения определенных цветов и лучшего пропускания дру- гих. Фильтры основных цветов (красного, зеленого и синего) могут быть ис- пользованы для получения цветных изображений от монохромных сканеров. Цветоделение. Процесс разделения цветных изображений на три основные ком- поненты: красную, зеленую и синюю. Цифровая обработка изображений. Цифровая обработка изображений име- ет дело с массивами чисел, которые представляют изображение. Различные алгоритмы используются для манипулирования данными изображения с це- лью получения желаемого результата. После окончания обработки получен- ные модифицированные данные используются для создания полутонового изо- бражения и его отображения. Шкала серого цвета. См. уровень серого цвета. Электронный луч. Перемещающийся пучок электронов, который создает изо- бражение на экране дисплея. ЭЛТ (CRT). Электронно-лучевая трубка; устройство отображения для ком- пьютерного монитора или телевизора. Язык ассемблера. Мнемонические коды, которые преобразуются в машинный язык программой ассемблера. Ассемблер — единственный язык, команды ко- торого могут непосредственно выполняться компьютером.
Литература Обработка изображений Baxes, Gregory A., Digital Image Processing, A Practical Primer, Prentice Hall, Engle- wood Cliffs, NJ, 1984. Castleman, Kenneth R., Digital Image Processing, Prentice Hall, Englewood Cliffs, NJ, 1979. CONRAC Division, CONRAC Corp., Raster Graphics Handbook, CA, 1980. Gonzalez and Wintz, Digital Image Processing, Addison-Wesley, Reading, MA, 1977. Holzmann, Gerald J., and AT&T Bell Labs Staff, Beyond Photography: The Digital Dark- room, Prentice Hall, Englewood Cliffs, NJ, 1988. Newman, W. M., and Sproull, R. E., Principles of Interactive Computer Graphics, McGraw-Hill, New York, 1979. Rosenfeld, A., and Как, A. C., Digital Picture Processing, 2d ed., Vols. 1 and 2, Academic Press, New York, 1982. Schacter, Bruce J. (ed.), Computer Image Generation, Wiley, New York, 1983. Sugiyama, Marc B., and Metcalf, Christopher D., Learning C — Programming Graphics on the Amiga and the Atari ST, Compute Publications, Inc., Greensboro, NC, 1987. VanDam, A., and Foley, J. D., Fundamentals of Interactive Computer Graphics, Addison- Wesley, Reading, MA, 1983. Программирование Borland International Inc., Turbo C Reference Guide and Turbo C User’s Guide, Scotts Valley, CA, 1987. Morgan, Christopher L., and Waite, Mitchell, 8086/8088 16-Bit Microprocessor Primer, BYTE/McGraw-Hill, Peterborough, NH, 1982. Willen, David C., and Krantz, Jeffrey I., 8088 Assembler Language Programming: The IBM PC, Howard W. Sams, Indianapolis, 1983. Обработка цифровых сигналов Oppenheim, A., and Schafer, R., Digital Signal Processing, Prentice Hall, Englewood Cliffs, NJ, 1975. Oppenheim, A. (ed.), Applications of Digital Signal Processing, Prentice Hall, Englewood Cliffs, NJ, 1978. Rabiner, L., and Gold B., Theory and Application of Digital Signal Processing, Prentice Hall, Englewood Cliffs, NJ, 1975. Компьютеры и микропроцессоры IBM Corp., Personal System/2 and Personal Computer BIOS Interface Technical Refer- ence, 68X2341, S68X-2341-00, May 1988. IBM Corp., Personal System/2 Hardware Interface Technical Reference, 68X2330, S68X- 2330-00, 1988. Intel Corp., iAPX 86/88, 186/188 User’s Manual, Programmer’s Reference, CA, 1986. Intel Corp., Microprocessor and Peripheral Handbook, Volume 1 — Microprocessor, CA, 1987.
Литература. 437 Журнальные статьи Ciarcia, Steven A., "Building the ImageWise Video Digitizer”, part 1 and part 2, BYTE magazine, May and June 1987. Ciarcia, Steven A., "Using the IniageWise Video Digitizer”, BYTE magazine, August 1987. Dawson, Benjamin M., "Introduction to Image Processing Algorithms”, ВYTEmagazine, March 1987. Heckbert, Paul, "Color Image Quantization for Frame Buffer Display”, ACM Computer Graphics Journal, Vol. 16, No. 3, July 1982. McManis, Charles, "Low-Cost Image Processing”, ВYTE magazine, March 1987. Nelson, Mark R., “LZW Data Compression”, Dr. Dobb’s Journal, October 1989. Rylander, Richard, “Solid Shape Drawing on the Commodore 64”, Dr. Dobb’s Journal, May 1985. Star, Jeffrey L., "Introduction to Image Processing”, ВYTE magazine, February 1985. White, Ronald G., "Compression Image Data with Quadtrees”, Dr. Dobb’s Journal, March 1987. Wilton, Richard, "Programming the Enhanced Graphics Adapter”, BYTE magazine, Fall 1985. Wilton, Richard, “PS/2 Video Programming”, ВYTE magazine, Fall 1987. Wilton, Richard, “VGA Video Modes”, ВYTE magazine, Fall 1988.
Приложение 1 Перечень функций PC BIOS Внутреннее прерывание 10Н - Видеорежим Функции ООН Установка режима 01Н Установка типа курсора 02Н Установка позиции курсора озн Чтение позиции курсора 04Н Чтение позиции светового пера 05Н Выбор активной страницы дисплея 06Н Просмотр активной страницы вверх 07Н Просмотр активной страницы вниз 08Н Чтение атрибута/символа текущей позиции 09Н Запись атрибута/символа курсора ОАН Запись символа в текущую позицию ОВН Установка цвета палитры ОСН Запись точки 0DH Чтение точки ОЕН Телетайпная запись в активную страницу 0FH Чтение текущего видеосостояния ЮН Установка регистра палитры Подфункции ООН 01Н 02Н ОЗН 07Н 08Н 09Н ЮН 12Н 13Н 15Н 17Н 1АН 1ВН Установка отдельного регистра палитры Установка регистра overscan Установка регистров палитры и overscan Флаг интенсивность/мерцание Чтение отдельного регистра палитры Чтение регистра overscan Взять все регистры палитры и overscan Установка отдельного цветового регистра Установка блока цветовых регистров Выбор цветовой страницы Чтение отдельного цветового регистра Чтение блока цветовых регистров Чтение состояния цветовой страницы Сумма цветовых значений в серых оттенках
Приложение 1 439 ИН Генератор символов Подфункции ООН и ЮН Загрузить пользовательский алфавит 01Н и НН Установить ROM 8 х 12 монохромный набор 02Н и 12Н Установить набор ROM 8x8 двойных точек ОЗН Установить признак блоков 04Н и 14Н Установить ROM шрифт 8 х 16 20Н Установить внутреннее прерывание 1FH указатель шрифта 21Н Установить внутреннее прерывание 43Н пользовательский шрифт 22Н Установить внутреннее прерывание 43Н ROM шрифта 8 х 14 23Н Установить внутреннее прерывание 43Н ROM шрифта 8x8 24Н Установить внутреннее прерывание 43Н ROM шрифта 8 х 16 ЗОН Получить информацию о шрифте 12Н Попеременный выбор Подфункции ЮН Возвращает информацию об адаптере EGA 20Н Выбирает режим печати копии экрана 12Н Попеременный выбор Подфункции ЗОН Выбор линий сканирования для текстовых режимов 31Н Разрешение/запрещение загрузки палитры 32Н Разрешение/запрещение видео ЗЗН Разрешение/запрещение суммирования шкалы яркости 34Н Разрешение/запрещение эмуляции курсора 35Н Переключатель активного дисплея 36Н Видеоэкран вкл./выкл. 13Н Запись строки 14Н Загрузка LCD-символа шрифта/Установка LCD, заменяющая значение высокой интенсивности 15Н Возврат параметров физического дисплея для активного дисплея 1АН Чтение/запись кода комбинации дисплея 1ВН Взять информацию о функционировании/статусе 1СН Сохранение/перезапись видеосостояния
440 Приложение 1 Внутреннее прерывание Внутреннее прерывание Внутреннее прерывание 11Н — Определение оборудования 12Н — Определение размера памяти 13Н — Дискеты и жесткие диски Функции ООН Перезагрузка системы с гибкими дисками 01Н Чтение статуса последней операции 02Н Чтение сектора в память озн Запись сектора из памяти 04Н Проверка сектора 05Н Форматирование дорожки 08Н Чтение параметров дисковода 09Н Инициирование характеристик жесткого диска ОАН Чтение длинного сектора ОВН Запись длинного сектора ОСН Поиск 0DH Перезагрузка системы с жестким диском ОЕН Чтение буфера сектора 0FH Запись буфера сектора ЮН Получение статуса дисковода 11Н Перекалибровка дисковода 12Н Диагностика контроллера ОЗУ 13Н Диагностика контроллера дисковода 14Н Диагностика внутреннего контроллера 15Н Чтение типа DASD ЮН Изменение линии статуса дискеты 17Н Установка типа DASD для формата 18Н Установка типа носителя для формата 19Н Парковка головок lAH Форматирование накопителя типа ESDI Внутреннее прерывание 14Н — Асинхронные связи Функции ООН 01Н 02Н ОЗН 04Н 05Н Инициирование порта СОМ Посылка символа Получение символа Чтение статуса Расширенное инициирование Расширенный контроль порта СОМ Внутреннее прерывание 15Н — Системные вызовы
Приложение 1 441 Функции ООН 01Н 02Н ОЗН 0FH 21Н Включение мотора кассеты Выключение мотора кассеты Чтение кассеты Запись кассеты Периодическое прерывание единицы формата Подфункция — ООН Чтение POST ошибки в файле регистрации 01Н Запись POST ошибки в файле регистрации 40Н 41Н 42Н 43Н 44Н 4FH 80Н 81Н 83Н 84Н 85Н 86Н 87Н 88Н 89Н ЭОН 91Н СОН С1Н С2Н Чтение/Изменение профилей Ожидание внешнего события Запрос на выключение питания Чтение статуса системы Активация/дезактивация питания внутреннего модема Отсекание клавиатуры Открытие устройства Закрытие устройства Завершение процесса Поддержка джойстика Запрос системы о нажатии клавиши Ожидание Перемещение блока (расширенной памяти) Определение размера расширенной памяти Переключение процессора на защищенный режим Устройство занято Завершение прерывания Возврат параметров конфигурации системы Возврат расширенного адреса сегмента данных BIOS BIOS-интерфейс точечного устройства Подфункции ООН Разрешение/Запрещение точечного устройства 01Н Переустановка точечного устройства 02Н Установка скорости ОЗН Установка разрешения 04Н Чтение типа устройства 05Н Инициирование интерфейса точечного устройства 06Н Расширенные команды 07Н Инициирование драйвера устройства дальнего вызова сзн С4Н Разрешение/Запрещение задержки будильника Программируемый выбор опций (POS) Внутреннее прерывание 16Н — Клавиатура
442 Приложение 1 Функции ООН 01Н 02Н ОЗН 04Н 05Н ЮН ИН 12Н Чтение клавиатуры Статус нажатия клавиши Статус клавиши SHIFT Установка скорости передачи Регулировка нажатия и отпуска клавиши Запись клавиатуры Расширенное чтение клавиатуры Расширенный статус нажатия клавиши Расширенный статус клавиши SHIFT Внутреннее прерывание 17Н — Принтер Функции ООН Печать символа 01Н Инициирование порта принтера 02Н Чтение статуса Внутреннее прерывание 18Н — ROM BASIC Внутреннее прерывание 19Н — Программа начальной загрузки Внутреннее прерывание 1АН — Обслуживание системного таймера и часов реального времени Функции ООН 01Н 02Н ОЗН 04Н 05Н 06Н 07Н 08Н 09Н ОАН ОВН 80Н Чтение счетчика времени системного таймера Установка счетчика времени системного таймера Чтение часов реального времени Установка часов реального времени Чтение даты реального времени Установка даты реального времени Установка будильника реального времени Переустановка будильника реального времени Установка часов реального времени включения энергии Чтение будильника реального времени и статуса Чтение счетчика дней системного таймера Установка счетчика дней системного таймера Установка звукового мультиплексора
Приложение 2 Графический формат TIFF Спецификация 5.0 Ниже изложена полная спецификация формата TIFF 5.0. Она включена в книгу в виде справочного материала для программистов, которые хотят более глубоко изучить формат TIFF. Очень внимательно читайте приложения этой специфика- ции. Они содержат много информации как о формате TIFF, так и об изображении в целом. Спецификация воспроизведена с разрешения Aldus Corporation. Я бы хотел поблагодарить Aldus Corporation за разрешение напечатать эту специфика- цию. (С) Aldus Corporation 1990. Использовано с разрешения Aldus Corporation. Aldus(R), PageMaker (R) и Persuasion(R) — зарегистрированные торговые марки Aldus Corporation. AldusFreeHand(TM) — торговая марка Aldus Corporation. Все права зарезервированы. Предисловие Эта памятка подготовлена фирмами Aldus и Microsoft при взаимодействии с ве- дущими производителями сканеров и другими заинтересованными сторонами. Она не представляет обязательства ни Aldus, ни Microsoft по поддержке этого формата в каком-либо применении. При возникновении вопросов, связанных с этой памяткой, пожалуйста, направляйте их по адресам: Developers’ Desk Aldus Corporation 411 First Ave. South Suite 200 Seattle, WA 98104 (206) 622-5500 Windows Marketing Group Microsoft Corporation 16011 NE 36th Way Box 97017 Redmond, WA 98073-9717 (206) 882-8080 Пояснения Данная версия заменяет четвертую версию формата TIFF. Части, выделенные курсивом, являются новыми или существенно переработанными в этой версии. Новыми являются также Дополнения F, G и Н, но они не выделены курсивом. Основным расширением TIFF 5.0 является следующее: 1. Сжатие полутоновых и цветных изображений для лучшего использования дискового пространства (см. Дополнение F). 2. Классы формата TIFF — ограниченные подмножества TIFF, которые мо- гут упростить работу по применению формата TIFF. Вам, возможно, сле- дует просмотреть Дополнение G, прежде чем читать оставшиеся части этого документа. Фактически вы можете использовать Дополнение G как
444 Приложение 2 основное руководство и возвращаться к основному содержанию специфи- кации, когда необходимо детально рассмотреть структуры TIFF и опреде- ление полей. 3. Поддержка изображения с «цветовой палитрой». Смотрите описание класса Р в Дополнении G и описание нового поля ColorMap. 4. Два новых тега, которые могут быть использованы для более полного определения характеристик полных данных цвета RGB, и тем самым по- тенциально улучшить качество воспроизведения цветного изображения (см. Дополнение Н). Структура документа также немного изменена. В основном тексте специфи- кации (внутри нескольких категорий) теги перечислены в алфавитном порядке. Как обычно, были предприняты все попытки расширить функциональность та- ким образом, чтобы сохранить совместимость со старым программным обеспе- чением, использующим TIFF. В частности, большинство файлов TIFF 5.0 будут читаться программами, которые воспринимают TIFF 4.0 или даже более ранние варианты этой спецификации. Единственным исключением являются файлы, которые используют новую схему сжатия TIFF 5.0 LZW. В этом случае старые программы, конечно, работать не смогут. Мы благодарны всем рецензентам за их предложения. Особенно полезными были рекомендации Херба Вейнера из Kitchen Wisdom Publishing Company, Брэда Пилоу из Truevision и инженеров из Hewlett Packard и Quark. Крис Сеарс из Magenta Graphics предоставил информацию, которая включена в Дополнение Н. Краткий обзор Этот документ описывает формат TIFF — формат файла, основанного на тегах, который разработан для поддержки обмена изображений в цифровом виде. При определении полей прежде всего учитывались программы электронной верстки и смежные с ними программы, хотя, возможно, другие типы программ обработки изображения также смогут эффективно использовать формат TIFF. Общие положения, для которых был разработан формат TIFF, подразумева- ют, что программы сканирования или рисования создают TIFF-файл, который затем может быть прочитан и включен в «документ» или «публикацию». TIFF не является ни языком принтера, ни языком описания страниц, и он не рассма- тривается в качестве общего стандарта. Первоначальной целью разработки формата было создание мощной среды, в которой мог бы выполняться обмен данными изображения между програм- мами. Эта мощность требуется для того, чтобы использовать возможности раз- личных сканеров и аналогичных устройств. Поэтому TIFF спроектирован в ка- честве надмножества (superset) существующих форматов файлов изображений для настольных сканеров (а также программ рисования и всего того, что со- здает изображение, состоящее из элементов изображения) и будет непрерывно совершенствоваться по мере возникновения новых возможностей. Высокий прио- ритет был отдан структурированию данных таким образом, чтобы не возникало много проблем при возможных будущих добавлениях. Таким образом, TIFF был разработан в качестве расширяемого формата обмена данными.
1. Структура, 445 Хотя TIFF претендует в некотором роде на то, чтобы быть форматом, обла- дающим богатыми возможностями, его можно легко использовать для простых сканеров и также простых программ, так как разработчик программного обес- печения имеет дело только с возможностями, которые он использует. Предпола- гается, что TIFF является независимым от операционных и файловых систем, компиляторов и процессоров. Единственно важным допущением является то, что носитель данных поддерживает что-то вроде «файла», определенного как после- довательность 8-бит байтов, где байты пронумерованы от 0 до N. Наибольшим допустимым размером файла TIFF является файл длиной 2**32 байт. Так как TIFF использует указатели (байтовые смещения) произвольно, файл TIFF наи- более легко читается из устройства произвольного доступа, такого, как жесткий диск или дискета, хотя можно читать и записывать файлы TIFF на магнитную ленту. Для систем MS-DOS, UNIX и OS/2 расширение TIFF-файлов является «.TIF». Для файлов, используемых «Macintosh», рекомендуется «TIFF». При- нимаются и другие предложения. 1. Структура В формате TIFF индивидуальные поля идентифицируются с единым тегом. Это позволяет полям либо присутствовать, либо отсутствовать в файле. Более по- дробное объяснение приведено в Дополнении А. TIFF-файл начинается с 8-байтового «заголовка файла изображения», ко- торый указывает на один или более «каталог файла изображения». Каталоги файла изображения содержат информацию об изображении, так же, как и ука- затели на действительные данные изображения (см. рис. 1). Теперь мы опишем эти структуры более подробно. Заголовок файла изображения TIFF-файл начинается с 8-байтового заголовка файла изображения, содержа- щего следующую информацию: Байты 0-1: Первое слово файла, определяющего порядок байта, используемого в файле. Допустимыми являются следующие значения: «II» (4949Н) «ММ» (4D4DH) В формате «II» порядок байтов всегда идет от меньшего к большему как для 16-бит, так и для 32-бит целых. В формате «ММ» порядок байтов всегда идет от большего к меньшему как для 16-бит, так и для 32-бит целых. В обоих форматах символы строк записываются последовательно в ячейках памяти побайтно. Все читатели этого TIFF должны поддерживать оба порядка байтов (см. Дополнение G). Байты 2-3: Второе слово файла — «номер версии» формата TIFF. Этот но- мер — 42 (2АН) — не сравнивается с текущей версией спецификации фор-
446 Приложение 2 Заголовок 0 Порядок байта 2 Версия 4 Смещение нулевого 6 А IFD Элемент каталога X Тег Х+2 Тип Х+4 Длина Х+8 у Значение 0 Y Значение Рис. 1
Каталог файла изображения 447 Mama TIFF. Фактически номер версии формата TIFF (42) никогда не из- меняется и, возможно, никогда не изменится. Если когда-нибудь такое из- менение произойдет, это будет означать, что TIFF изменился настолько сильно, что читателям необходимо немедленно отказаться от него. Но- мер версии 42 был выбран из глубоких философских соображений. Он может быть использован как дополнительная проверка того, что это действитель- но TIFF-файл. TIFF-файл не имеет действительного числа версий. Это было точное созна- тельно разработанное решение. Во многих форматах файла поля принима- ют другие значения в зависимости от номера версии. Проблема заключается в том, что формат файла «стареет», становится все труднее определить, какие поля означают, от чего в данной версии или в более старом программ- ном обеспечении можно отказаться, если встречается файл с более новым номером версии. Мы хотим, чтобы поля TIFF имели долговременные или хо- рошо определенные значения, чтобы «старое» программное обеспечение мо- гло читать «новые» файлы TIFF. Суть этого заключается в том, чтобы затраты на программное обеспечение были как можно ниже, а программное обеспечение более надежно. Байты 4—7: Это длинное слово содержит байтовое смещение первого каталога файла изображения. Каталог может находиться в любом положении в файле после заголовка, но он должен начинаться с граничного слова. В частности, каталог файла изображения может следить за описанными им данными изображения. Читатели должны следить за указателями, куда бы они ни вели. (Термин «байтовое смещение» используется в этом документе, чтобы обратиться к позиции относительно начала файла. Первый байт файла имеет нулевое смещение.) Каталог файла изображения Каталог файла изображения (IFD) состоит из 2-байт счетчика входа (т. е. номера полей), за ним следует 12-байт поле входов, за ним следует 4-байт смещение для следующего каталога файла изображения (если IFD нет, то 0). Не забудьте записать четыре байта нулями после последнего IFD. Каждый 12-байт вход IFD имеет следующий формат: Байты 0-1 Байты 2-3 Байты 4-7 содержат тег для поля. содержат тип поля. содержат длину («счетчик» может быть заменен Байты 8-11 лучшим термином) поля. содержат смещение значения, смещение файла значения (в байтах) для поля. Ожидается, что значение начина- ется с граничного слова. Соответствующее смещение значения будет, таким образом, четным числом. Это смещение файла может указывать на что угодно в файле, включая область после данных изображения. С помощью тега входы в IFD должны быть классифицированы в порядке их возрастания. Обратите внимание на то, что это не тот порядок, по которому опи-
448 Приложение 2 саны поля в этом документе. В Дополнении Е показан порядок нумерации тегов. Значения, на которые указывает каталог входов, должен быть в определенном порядке в файле. Для того чтобы сохранить время и пространство, смещение значений интер- претируется как содержащее значение, вместо указывающего на значение, если это значение соответствует 4 байтам. Если значение меньше 4 байтов, оно вы- равнивается влево в пределах 4-6айтового смещения значения, т. е. хранится в младших байтах. Для того чтобы определить, соответствует ли значение 4 бай- там или нет, надо посмотреть на тип и длину поля. Длина определяется в значениях типа данных, а не полного числа байтов. Например, единичное 16-байтовое слово (SHORT) имеет длину, равную 1, а не 2. Ниже приведены типы данных и их длины: 1 = BYTE 2 = ASCII 3 = SHORT 4 = LONG 5 = RATIONAL 8-битовое беззнаковое целое. 8-битовые байты, которые хранят коды ASCII, последний байт должен равняться NULL. 16-битовое (2-байтовое) беззнаковое целое. 32-битовое (4-байтовое) беззнаковое целое. Два LONG’s: первое представляет числитель дроби, второе — знаменатель. Значение части длины входа поля ASCII содержит NULL. Если необходимо выравнивание, в длину не включается выравнивающий байт. Отметим, что здесь нет «байта счетчика», как это принято в строках на языке Паскаль. Часть длины поля заботится об этом. NULL не является строго необходимым, но он упрощает многие процедуры для программистов на языке Си. Читатель должен проверить тип, чтобы убедиться, что это то, чего он ожида- ет. В настоящее время TIFF допускает более одного обоснованного типа для по- лей. Например, указывалось на то, что функции «ImageWidth» и «ImageLength» имеют тип SHORT. Даже сейчас возможны очень большие изображения с более чем 64К строк и столбцов. Вместо того чтобы добавлять параллельные теги типа LONG для этих полей, лучше допустить использование типа как SHORT, так и LONG для поля «ImageWidth» и аналогичных ему полей (см. Дополнение G для специальных рекомендаций). Отметим, что может существовать более одного IFD. Принято, что каждый IFD определяет «подфайл». Одним из потенциальных применений подфайлов является его способность описывать «подызображение», что каким-то образом относится к основному изображению, как, например, версия изображения с уменьшенным разрешением. Если вы еще этого не сделали, вы можете изучить образец изображений фор- мата TIFF в Дополнении G. 2. Определения Отметим, что, как было описано выше, структура TIFF так или иначе не являет- ся конкретной для программ изображения. Это только определение самих полей, которые описывают изображение. Прежде чем начать определение полей, мы
3. Поля 449 должны определить некоторые основные понятия. Изображение — это массив прямоугольной формы «элементов изображения», каждый из которых состоит из одного или более «квантов информации». При работе с монохромными данны- ми мы имеем один квант на элемент изображения, так что «квант» и «элемент изображения» являются взаимозаменяемыми. Цветовые данные RGB содержат 3 кванта на элемент изображения. 3. Поля Ниже описаны поля, определенные в этой версии формата TIFF. В будущих версиях может быть добавлено много полей — по возможности они будут до- бавлены таким образом, чтобы не разрушить старое программное обеспечение, которое будет встречаться в более новом TIFF-файле. Документация каждого поля содержит название поля (совершенно произ- вольно, но так, чтобы было удобно), значение тега, тип поля, ожидаемое число значений (N), примечания, описание поля и, если имеются, значения по умолча- нию. Если поля не существуют, читатель должен допустить значения по умол- чанию. «Отсутствие значения по умолчанию» («No default») не означает, что про- грамма, создавшая данный TIFF-файл (которую в дальнейшем мы будем назы- вать «автор»), не уделила внимания этому тегу. Это означает, что для него нет значения по умолчанию. Если автор имеет основание полагать, что чита- тели (программы, которые используют данный файл) заинтересованы значе- нием этого поля, то он должен заполнить его соответствующим значением. Читатели TIFP-файла могут делать все, что захотят, если они столкнутся с пропущенным незаполненным полем, информация о котором им требуется, за исключением отказа от импорта файла. Например, если автор не заполня- ет поле «Photochromiclnterpretation», некоторые программы будут толковать изображение «верно», а другие будут отображать его на экране инвертирован- ным. Это очень плохо, и авторы должны обратить внимание на то, чтобы этого не случилось. Поля сгруппированы в несколько категорий: базовые, информационные, фак- симильные, для хранения и поиска документов, а также более не рекоменду- емые для использования (устаревшие). В будущей версии этой спецификации некоторые из этих категорий, возможно, будут выделены в отдельные сопро- водительные документы. Из большого числа полей, описанных в этом документе, большинство не являются обязательными. В Дополнении G представлен список обязательных полей и примеры того, как объединить поля в правильные и полезные файлы формата TIPF. Базовые поля Базовыми называются поля, которые являются фундаментальными по отноше- нию к архитектуре элемента изображения или видимым характеристикам изо- бражения. 29-3
450 Приложение 2 BitsPerSample Tag = 258(102) Type = SHORT N = SamplesPerPixel Число бит в образце. Обратите внимание на то, что этот тег допускает разное число бит в образце для каждого образца соответствующего элемента изобра- жения. Например, цветовые данные RGB могут использовать различное число бит в образце для каждой из трех цветовых плоскостей. Большинство файлов RGB будут иметь такое же число BitsPerSample в каждом образце. Даже в этом случае убедитесь, что включены все три входа. Запись числа «8» вместо требуемого «8, 8, 8» создаст сложности для работы других полей. Значение по умолчанию равно 1. Смотрите также поле «SamplesPerPixel». Color Мар Tag = 320(Ц0) Туре = SHORT N — 3*(2**BitsPerSample) Этот тег определяет таблицу цветов формата RGB в качестве палитры цветных изображений. Цвет элемента изображения используется для форми- рования индекса во всех трех компонентах. Например, палитра элемента цве- тового изображения, имея значение 0, будет производиться на экране дисплея в соответствии с нулевым входом для красной, зеленой и синей компоненты. Составляющие цвета размещаются последовательно. Красные входы идут первыми, за ними следуют зеленые, далее синие. Длина каждой составляю- щей равна 2**BitsPerSample. Вход ColorMap для 8-битовой палитры цветового изображения будет иметь 3*256 элементов. Ширина каждого элемента рав- на 16 бит, как и подразумевает тип SHORT. Значение 0 представляет ми- нимальную интенсивность, а значение 65535 представляет максимальную ин- тенсивность. Черный цвет представлен значением 0,0,0, белый — значением 65535, 65535, 65535. Цветные таблицы функционируют как «таблицы преобра- зований», изменяя значения элементов изображения от 0 до 2**BitsPerSample-l в триаде RGB. Поле ColorResponseCurves может быть использовано наряду с полем Color- Map для дальнейшего уточнения RGB триад в поле ColorMap. Однако значе- ние поля ColorResponseCurves, задаваемое по умолчанию, будет достаточным в большинстве случаев. Смотрите также поле Photometriclnterpretation — цветовая палитра. Значение по умолчанию отсутствует. Поле ColorMap должно быть включено во все изображения с цветовой пали- трой. ColorResponseCurves Tag = 301(12D) Type = SHORT N = 3*(2**BitsPerSample)
3. Поля 451 Этот тег определяет три компоненты цвета, одна — для красного, другая — для зеленого и третья — для синего цвета. Красные компоненты являются пер- выми, за ними следуют зеленые компоненты, за ними — синие компоненты. Длина каждой компоненты равна 2**BitsPerSample; при этом используется зна- чение поля BitsPerSample, соответствующее первоначальному. Ширина каждой компоненты равна 16 бит, как подразумевает тип SHORT. Значение 0 пред- ставляет минимальную интенсивность, значение 65535 — максимальную. Черный цвет представлен значением 0,0,0, белый —значением 65535, 65535, 65535. Таким образом, элемент поля ColorResponseCurves для RGB-данных, где каждый из образцов имеет размер 8 бит, будет иметь 3x256 элементов типа SHORT. Это поле предназначено для уточнения содержания цветных изобра- жений в формате RGB. Для дальнейшей информации смотрите Дополнение Н. Значение по умолчанию: для каждой компоненты, основанной на стандарте NTSC, рекомендуется гамма, равная 2,2. Compression Tag = 259(103) Type = SHORT N = 1 1 = Отсутствие сжатия, но данные упакованы в байты настолько плотно, на- сколько возможно, все биты использованы, за исключением конца ряда. Бай- ты хранятся как массив типа BYTE, для BitsPerSample <= 8, SHORT, если 8 < BitsPerSample <= 16 и LONG, если 16 < BitsPerSample <= 32. Порядок байта данных больше 8 бит должен быть совместимым с порядком, опреде- ленным в заголовке файла формата TIFF (байты 0 и 1). Для файлов формата «И» младшие значащие байты будут находиться перед старшими значащими байтами. Для файлов формата «ММ» наблюдается обратная картина. Если число битов в образце не во второй степени и вы хотите оставить некото- рое пространство для повышения производительности, вы можете использо- вать следующую степень выше двух. Например, если ваши данные могут быть представлены в 6 битах, вы можете обозначить, что это размер 8 бит. Требу- ется, чтобы ряды начинались на границах байтов. Поэтому число байтов в ряду равно (ImageWidth*SamplesPerPixel*BitsPerSample + 7)/ 8, (с использо- ванием целочисленной арифметики), для значения PlanarConfiguration = 1. Число байтов в ряду равно (ImageWidth*BitsPerSample + 7)/ 8 для Planar- Configuration = 2. Некоторые графические системы используют 16- или 32-разрядное выравни- вание. Перед использованием несжатых TIFF-файлов в графических процеду- рах этих систем их следует скопировать в специально выравненные буферы строк. 2 = Сжатие по модифицированному одномерному методу Хаффмена (CCITT Group 3). Смотрите Дополнение В. Поле BitsPerSample должно быть равно 1, так как этот тип сжатия определяется только для двухуровневых изобра- жений. Когда распаковываются данные, которые были сжаты с параметром Compression = 2, вы должны перевести белые отрезки в нулевые значения, а черные — в значения, равные 1. Таким образом, обычное значение поля Photometricinterpretation для всех типов сжатия равно нулю (WhitelsZего). 29*
452 Приложение 2 Если читатель получит значение поля Photometricinterpretation, равное 1, (BlacklsZero), то в этом случае изображение будет выводиться на экран и печататься с инверсией белых и черных точек. 5 = Сжатие по методу LZW для полутоновых, преобразованных цветных и полных цветных изображений. Смотрите Дополнение F. 32773 = Сжатие по методу PackBits, простая схема для 1-битовых изображе- ний. Смотрите Дополнение С. Сжатие данных применяется только к данным растра изображения, на ко- торый указывает StripOffsets. Вся остальная информация формата TIFF не за- трагивается. Значение по умолчанию равно 1. Gr ay Resp onse C ur ve Tag = 291(123) Type = SHORT N = 2**BitsPerSample Поле GrayScaleResponseUnits определяет точность информации, содержа- щейся в компоненте. Так как оптическая плотность определяется с помо- щью дробных чисел, этот тег необходим для того, чтобы узнать, как преобра- зовать информацию, записанную в виде целых чисел. Например, если поле GrayScaleResponseUnits равно 4 (0,0001) и значение поля GrayScaleResponseCurve для уровня яркости 4 равно 3455, то результирующее значение оптической плот- ности будет равно 0,3455. Оптические денситометры обычно измеряют плот- ности в диапазоне от 0,0 до 2,0. Если шкала плотности известна для данных в TIFF-файле и для устройства вывода, то может быть выполнено грамотное преобразование входных данных в выходные данные устройства вывода. Например, выход может быть выпол- нен так же, как и вход. В дополнение, если входное изображение имеет плохой контраст (как можно увидеть из шкалы плотности), то может быть выполнено соответствующее повышение контрастности. Это поле функционирует как таблица преобразований, масштабируя значе- ния от 0 до 2**BitsPerSample-l в определенном значении плотности. Нулевой элемент массива GrayResponseCurve используется для определения серого зна- чения всех элементов изображения, имеющих значение 0, первый элемент мас- сива GrayResponseCurve используется для определения значения серого цвета всех элементов изображения, имеющих значение 1 и так далее до значения 2**BitsPerSample-l. Если ваши данные в действительности имеют 7 бит, но вы добавляете 1 бит к каждому элементу изображения, чтобы выравнять его до 8-битового числа, то все работает. Если данные выравнены по старшему по- рядку, половина элементов поля GrayResponseCurve (возможно, нечетные чи- сла) никогда не будут использованы, но они ничего не повредят. Если данные выравниваются по младшему порядку, значения элементов изображения будут находиться между 0 и 127. Что произойдет с вашей компонентой при значе- ниях от 128 до 255, не имеет значения. Обратите внимание на то, что вырав- нивание в младшем порядке — это, наверное, не самая хорошая идея, так как не все программы рассматривают поле GrayResponseCurve. Также запомните,
3. Поля 453 что сжатие по методу LZW производит ту же пропорцию сжатия, независи- мо от того, выравнены ли данные в старшем или младшем порядке. Допускается, что даже двухуровневые (1-битовые) изображения могут иметь поле GrayResponseCurve. В этом случае данное поле будет иметь 2 зна- чения. Однако необходимо отметить, что читателям формата TIFF В не тре- бовалось уделять внимание полю GrayResponseCurve. Смотрите также Допол- нение G. Если поля GrayResponseCurve и Photometriclnterpretation существуют в IFD, значения поля GrayResponseCurve преобладают над значениями поля Photomet- riclnterpretation. Но лучше все-таки заполнять оба поля, так как в некоторых применениях не используется поле GrayResponseCurve. Программисты могут приобрести Kodak Reflection Density Guide (номер no каталогу 1465947), стоимостью 10 долл, в пунктах ксерокопирования для опре- деления приемлемых значений оптической плотности для своих сканеров. Если это слишком сложно, мы предлагаем компоненту, которая является линей- ной для интенсивности/отражательной способности. Расчет отражатель- ной способности, исходя из плотности, дает R = l/pow(10,D). Расчет плотно- сти, исходя из отражательной способности, дает D — loglO(l/R). Типичное 4- битовое поле GrayResponseCurve может выглядеть таким образом: 2000, 1177, 875, 699, 574, 477, S98, 331, 27S, 222, 176, 1S5, 97, 62, SO, 0. Предполагается, что поле Gray Response Unit равно 3. Такая компонента будет согласовываться с полем Photometriclnterpretation = 1. Смотрите также поля GrayResponseUnit, Photometriclnterpretation и Color- Map. Gr ay Resp onseU nit Tag = 290(122) Type = SHORT N = 1 1 = число представляет десятые доли единицы. 2 = число представляет сотые доли единицы. 3 = число представляет тысячные доли единицы. 4 = число представляет десятитысячные доли единицы. 5 = число представляет стотысячные доли единицы. Преобразует поле GrayResponseCurve. Смотрите также поле GrayResponseCurve. Значение по умолчанию равно 2. Однако для большей точности мы рекомен- дуем использовать значение 3. ImageLength Tag = 257(101) Type = SHORT or LONG N = 1 Длина (высота) изображения в элементах изображения (Y: вертикаль). Чи- сло рядов (иногда описанных как «линия сканирования») в изображении. Смотрите также поле ImageWidth. Значения по умолчанию отсутствуют.
454 Приложение 2 ImageWidth Tag = 256(100) Type = SHORT or LONG N = 1 Ширина изображения в элементах изображения (X: горизонталь). Число ко- лонок в изображении. Смотрите также ImageLength. Значения по умолчанию отсутствуют. NewSu bfile Type Tag = 25J(FE) Type = LONG N = 1 Замещает старое поле SubfileField вследствие ограничений в определении то- го поля. Общее определение типа данных, содержащихся в этом «подфайле». Это поле состоит из установки набора 32-битовых флагов. Ожидается, что неиспользованные биты будут равны 0. Нулевой бит является битом низкого порядка. На сегодняшний день определены: Бит 0 равен 1, если изображение является вариантом другого изображения с уменьшенным разрешением в этом TIFF-файле, в противном случае он равен 0. Бит 1 равен 1, если изображение является отдельной страницей в многостраничном изображении (смотрите описание тега в PageNumber), иначе бит равен 0. Бит 2 равен 1, если изображение определяет прозрачную маску для другого изображения в этом файле формата TIFF. Значение поля Photometricinterpretation должно быть равно 4, обозначая прозрачную маску. Эти значения определены как битовые флаги, так как они достаточно неза- висимы друг от друга. Например, было бы полезно иметь 4 изображения в еди- ничном файле TIFF: изображение полного разрешения, изображение с умень- шенным разрешением, прозрачную маску для изображения полного разреше- ния и прозрачную маску для изображения с уменьшенным разрешением. Ка- ждое из этих четырех изображений будет иметь различные значения поля NewSubfileType. Значение по умолчанию равно 0. Photometricinterpretation Tag = 262(106) Type = SHORT N =1 О = Для двухуровневых и полутоновых изображений: 0 представлен как белый цвет. 2**BitsPerSample-l представлен как черный цвет. Если существует поле GrayResponseCurve, оно превалирует над значением поля Photometricin- terpretation, хотя надежнее установить оба поля, так как некоторые старые
3. Поля 455 версии могут игнорировать поле GrayResponseCurve. Это действительное значение для сжатия = 2. 1 = Для двухуровневых и полутоновых изображений: 0 представлен как чер- ный цвет, 2**BiisPerS ample-1 представлен как белый цвет. Если существу- ет GrayResponseCurve, оно превалирует над значением Photometriclnterpreta- Поп, хотя надежнее сравнить их, так как некоторые старые версии могут не учитывать GrayResponseCurve. Если это значение определено для сжа- тия = 2, то изображение будет выводиться на экран и печататься инвер- тированно. 2 = RGB. В модели RGB цвет описывается как комбинация трех основных цве- тов (красного, зеленого и синего) в особых концентрациях. Для каждого из трех образцов 0 представляет собой минимум интенсивности, a 2**BitsPer- Sample-1 представляет собой максимум интенсивности. Таким образом, зна- чение RGB (0,0,0) отображает черный цвет, а значение (255,255,255) — белый цвет, предполагая, что образцы 8-битовые. Если поле PlanarConfiguration = 1, то образцы хранятся в указанном порядке: сначала красный, затем — зеле- ный и синий. При значении поля PlanarConfiguration = 2 плоскости образцов поля StripOffsets хранятся в следующем порядке: сначала — красная плос- кость StripOffsets, затем — зеленая плоскость StripOffsets и синяя плоскость StripOffsets. Поле ColorResponseCurves может быть использовано для полной очистки или для изменений цветового баланса изображения RGB без изменения зна- чений самих элементов изображения. 3 = «Цветовая палитра». В этом режиме цвет описывается с помощью еди- ничного образца. Образец используется в качестве индекса в ColorMap. Он используется также для индексации в каждой из красных, зеленых и синих компонент таблиц, чтобы восстановить триаду RGB, определяя действи- тельный цвет. Когда используется значение поля Photometricinterpretation, также должны быть определены цветные компоненты. Поле SamplesPer- Pixel должно быть равно 1. 4 = Прозрачная маска. Это означает, что данное изображение используется для определения области неправильной формы другого изображения в том же TIFF-файле. Поля SamplesPerPixel и BitsPerSample должны быть равны 1. Рекомендуется сжатие по методу PackBits. Бит со значением 1 опреде- ляет внутреннюю часть области, нулевой бит определяет внешнюю часть области. Transparency Mask должен иметь то же значение ImageLength и ImageWidth, что и основное изображение. Пользовательская программа может использовать маску, чтобы опреде- лить, какие части изображения отображать на экране. Элементы основ- ного изображения, соответствующие битам со значением 1 в прозрачной маске, отображаются на экране или принтере, а элементы, которые соот- ветствуют нулевым битам в маске, не отображаются на экране. Для обобщения понятия прозрачной маски можно ввести понятие частич- ной прозрачности, но пока еще не ясно, будет ли такая информация полезна для программ электронной верстки.
456 Приложение 2 Значение но умолчанию отсутствует. Это означает, что если вам требуется вывести на экран или напечатать «нормальное* изображение, а не инвертиро- ванное, вы должны установить это поле. Не следует полагаться на программы, устанавливающие поля по умолчанию. Значение поля Photomeiriclnierpretaiion, равное 0, рекомендуется для двухуровневых (за исключением сжатия, равного 2) и полутоновых изображений из-за популярных интерфейсов для изменения яркости и контраста изображений. PlanarConfiguration Tag = 284(11С) Туре = SHORT N = 1 1 = Значения образца последовательно накапливаются для каждого элемента изображения таким образом, что образуется единая плоскость изображения. Для определения порядка образцов в пределах данных элементов изображе- ния смотрите поле Photometriclnterpretation. Таким образом, для данных RGB данные накапливаются следующим образом RGBRGBRGB... и так далее. 2 = Образцы накапливаются в отдельных «плоскостях образца». Значения в по- лях StripOffsets и StripByteCounts затем располагаются как двумерный мас- сив с рядами SamplesPerPixel и StripsPerlmage.(Колонки для ряда, равного О, накапливаются первыми, за ними следуют колонки ряда, равного 1, и так да- лее.) Поле Photometriclnterpretation описывает типы данных, накопленные в каждой плоскости образца. Например, данные RGB накапливаются с красны- ми значениями образцов на одной плоскости, с зелеными — на другой плос- кости, с синими — на третьей плоскости. Если поле SamplesPerPixel равно 1, то поле PlanarConfiguration не должно быть включено. Значение по умолчанию равно 1. Смотрите также поля BitsPerSample и SamplesPerPixel. Predictor Tag = 317(13D) Type = SHORT N =1 Используется, когда сжатие равно 5 (LZW). Смотрите Дополнение F. 1 = Перед кодированием не используются схемы предсказаний. Значения по умолчанию равны 1. ResolutionUnit Tag = 296(128) Type = SHORT N = 1 Используется с полями XResolution и YResolution.
3. Поля 457 1 = Нет абсолютной единицы измерения. Используется для изображений, ко- торые имеют неквадратное соотношение геометрических размеров, но не име- ют значащих абсолютных размеров. Недостатком поля ResolutionUnit sz 1 является то, что различные программы будут импортировать одно изо- бражение с разными размерами. Даже если решение будет произвольным, лучше использовать шкалу типа точки на дюйм или точки на сантиметр и выбрать значение полей XResolution и YResolution, чтобы соотношение геометрических размеров было правильным и максимальные размеры изо- бражения составляли порядка 4 дюйм (значение «4» выбрано произвольно). 2 = Дюйм. 3 = Сантиметр. Значения по умолчанию равны 2. Смотрите также поля XResolution и YResolution. RowsPerStrip Tag = 278(116) Type = SHORT or LONG N = 1 Число рядов в полосе. Данные изображения организованы в полосы для бо- лее быстрого доступа к отдельным рядам, когда данные сжаты — хотя это по- ле является правильным, даже если данные не сжаты. Поля RowsPerStrip и ImageLength вместе несут информацию о числе полос в целом изображении. Уравнение имеет вид: StripsPerlmage = (ImageLength + RowsPerStrip —)/RowsPerStrip, принимая арифметическое целое. Отметим, что значения либо типа SHORT, либо типа LONG могут исполь- зоваться для определения поля RowsPerStrip. Значения типа SHORT могут ис- пользоваться для малых TIFF-файлов. Однако необходимо отметить, что более ранняя спецификация TIFF требует, чтобы значения типа LONG и некоторое программное обеспечение не предполагали значения типа SHORT. Для дальней- ших рекомендаций смотрите Дополнение G. Значение по умолчанию равно 2**32-1 и является бесконечным, т. е. пол- ное изображение представлено одной полосой. Мы, однако, не рекомендуем един- ственную полосу. Выберите поле RowsPerStrip таким образом, чтобы каждая полоса составляла 8К байт, даже если данные не сжаты, так как это делает буферизацию более простой для читателей. Коэффициент «8К> произволен, но функционирует хорошо. Также смотрите поля ImageLength, StripOffsets, StripByteCounts. SamplesPerPixel Tag = 277 (115) Type = SHORT N =1
458 Приложение 2 Число образцов в элементе изображения. Поле SamplesPerPixel равно 1 для двухуровневых, полутоновых изображений и изображений с цветовой пали- трой. Поле SamplesPerPixel равно S для изображений RGB. Значение по умолчанию равно 1. Смотрите также поля BitsPerSample и Photo- metriclnterpretation. StripByteCounts Tag = 279 (117) Type = SHORT или LONG N = StripsPeErlmage для PlanarConfiguration, равного 1. = SamplesPerPixel*StripsPerImage для PlanarConfiguration, равного 2. Число байтов в полосе для каждой полосы. Наличие этого поля существенно упрощает работу сжатых данных, если приемлем размер полосы. Значение по умолчанию отсутствует. Смотрите также поля StripOffsets и RowsPerStrip. Strip Offsets Tag = 273 (111) Type = SHORT или LONG N = StripsPerlmage для PlanarConfiguration, равного 1. = SamplesPerPixel*StripsPerImage для PlanarConfiguration, равного 2. Байтовые смещения для каждой отдельной полосы. Смещение определено по отношению к началу TIFF-файла. Это означает, что каждая полоса имеет по- ложение, независимое от положений других полос. Это свойство может быть полезно для применения в издательском деле. Только с помощью этого поля читатели могут найти данные изображения и, следовательно, оно должно суще- ствовать. Отметим, что значения либо типа SHORT, либо типа LONG могут быть ис- пользованы для определения смещения полос. Значения типа SHORT могут ис- пользоваться для малых TIFF-файлов. Необходимо отметить, что более ран- няя спецификация TIFF требовала, чтобы смещение внутри полосы имело тип LONG, поэтому некоторые программы не поддерживают величины типа SHORT. Для дальнейших рекомендаций смотрите Дополнение G. Значения по умолчанию отсутствуют. Смотрите также поля StripByteCounts и RowsPerStrip. XResolution Tag = 282(11А) Type = RATIONAL N = 1 Число элементов изображения в поле ResolutionUnit в направлении X, то есть в направлении ImageWidth. Конечно, необязательно, чтобы изображение было отпечатано в размере, обозначенном этим параметром. Ответственность за эту информацию лежит полностью на программе.
3. Поля 459 Значение по умолчанию отсутствует. Смотрите также поля YResolution и ResolutionUnit. YResolution Tag = 283(11В) Type = RATIONAL N = 1 Число элементов в изображении в поле ResolutionUnit в направлении Y, т. е. в направлении ImageLength. Значение по умолчанию отсутствует. Смотрите также поля XResolution и ResolutionUnit. Информационные поля Информационными называются поля, которые снабжают пользователя полез- ной информацией, такой, как например, откуда пришло изображение. В основ- ном это поля кода ASCII. Программа может использовать диалоговое окно типа «Моте Info...» («Дополнительной информации») для вывода такой информации на экран. Artist Тад = 315 (13В) Туре = ASCII Человек, который создает изображение. Если вам необходимо присоединить знак Copyright к изображению, то это как раз то поле, где это можно выполнить. Фактически вы можете запи- сать содержание поля сразу после 8-битового заголовка TIFF. Необходимо толь- ко убедиться, что указатели IFD и указатели поля соответственно установ- лены. DateTime Tag = 306(132) Type = ASCII N = 20 Дата и время создания изображения. Можно использовать формат «YYYY: MM:DD HH:MM:SS>, если часы имеют 24-часовой цикл. Длина строки, включая NULL, составляет 20 байт. HostComputer Tag = 316(13С) Туре = ASCII «ENIAC» или что-либо еще. ' Смотрите также поля Make, Model, Software.
460 Приложение 2 ImageD es crip t ion Tag = 270(10E) Type = ASCII Например, пользователю необходимо присоединить подпись «пикник 1988 го- да» к изображению. В этом случае можно использовать это поле. Это то, что в полиграфической промышленности называется «строка, отлитая на лино- типе». Маке Tag = 271(10F) Type = ASCII Производитель сканеров, цифровых видеопреобразователей и т. д. Смотрите также поля Model, Software. Model Tag = 272 (110) Type = ASCII Название/номер модели сканера, видеопреобразователя и т. д. Этот тег предназначается только для информации потребителя. Смотрите также поля Make, Software. Software Tag = 305(131) Type = ASCII Название и номер версии программного пакета, который создал изобра- жение. Этот тег предназначен только для информации потребителя. Смотрите также поля Make, Model. Факсимильные поля Факсимильные поля могут быть полезны для вас, если вы используете TIFF для хранения факсимильных записей в «необработанном» виде. Они не рекоменду- ются к использованию при взаимообмене в программах электронной верстки. Compression (основной тег) Tag = 269(103) Туре = SHORT N = 1 3 = Сжатие по факсимильно-совместимому методу (CCITT Group 3) такое же, какое определено в «Standardization of Group 3 facsimile apparatus for document transmission», Recommendation T.4, Volume VII, Fascicle VII.3, Terminal Equipment and Protocols for Telematic Services, The International Telegraph and Telephone Consultative Committee (CCITT), Женева, 1985 г.,
3. Поля 461 стр. 16-31. Каждая полоса должна начинаться на границе байта. (Напомним, что изображение может находиться в единственной полосе). Ряды, которые следуют за первым рядом полосы, начинаются на границе байта. Данные хранятся как байты, а не как слова — не разрешается перестановка байтов. Смотрите поле Group30ption для опций группы 3, таких, как одномерное и двумерное кодирование. 4 = Сжатие по факсимильно-совместимому методу (CCITT Group 4) точно та- кое же, как и определено в «Facsimile Coding Schemes and Coding Control Functions for Group 4 Facsimile Apparatus», Recommendation T.6, Volume VII, Fascicle VII.3, Terminal Equipment and Protocols for Telematic Services, The International Telegraph and Telephone Consultative Committee (CCITT), Жене- ва, 1985 г., стр. 40-48. Каждая полоса должна начинаться на границе байта. Ряды, которые следуют за первым рядом полосы, начинаются на границе бай- та. Данные хранятся как байты, а не как слова. Смотрите поле Group4Option для опций группы 4. Group3Option Tag = 292(124) Type = LONG * N = 1 Смотрите поле Compression = 3. Это поле составлено из набора 32 флаг говых битов. Ожидается, что неиспользованные биты будут равны 0. Бит 0 — это бит младшего порядка. Возможно, файл будет прочитан неточно, если вам неизвестно любое значение бита этого поля. Бит 0 равен 1 для двумерного кодирования (кроме этого допускается одномер- ное кодирование). В случае двумерного кодирования, если определено более одной полосы, каждая полоса должна начинаться с одномерной кодирован- ной линии, т. е. поле RowsPerStrip будет произведением «Parameter К», как определено в спецификации CCITT. Бит 1 равен 1, если используется режим без сжатия. Бит 2 равен 1, если заполненные биты добавляются при необходимости перед кодами EOL так, чтобы EOL всегда заканчивался на границе байта, при этом обеспечивая ео1-согласование (последовательность кодов конца строки) 1 бай- та, продолженного с помощью нулевого полубайта: хххх-0000 0000-0001. Значение по умолчанию равно 0 для основного 1-мерного кодирования. Смотрите также поле Compression. Group4Option Tag = 293(125) Type = LONG N = 1 Смотрите поле Compression = 4- Это поле составлено из набора 32 флаговых битов. Ожидается, что неиспользованные биты будут равны нулю. Бит 0 — это
462 Приложение 2 бит младшего порядка. Возможно, файл будет прочитан неточно, если вам неиз- вестно любое значение бита этого поля. Изучаются схемы кодирования яркости и цвета и вскоре они будут добавлены к этой версии. Для двумерного кодирования каждая полоса кодируется таким образом, как если бы это было отдельное изображение. В частности, каждая полоса начи- нается на границе байта, и кодирование первого ряда полосы осуществляется независимо от предыдущих рядов, используя горизонтальные коды, как если бы предыдущие ряды были полностью белыми. Каждая полоса заканчивается 24- битовым блоком конца факсимиле (EOFB). Бит 0 не используется. Бит 1 равен 1, если используется режим без сжатия. Значение по умолчанию равно 0 для основных двумерных бинарных сжатий. Смотрите также поле Compression. Поля хранения и поиска документации Эти поля могут быть полезны при хранении и поиске документации. Они не рекомендуются для использования при взаимообмене в программах электронной верстки. DocumentName Tag = 269(10D) Type = ASCII Название документа, из которого сканируется изображение. Смотрите также поле PageName. PageName Tag = 285(11D) Type = ASCII Название страницы, из которой сканируется изображение. Смотрите также поле DocumentName. Значение по умолчанию отсутствует. PageNumber Tag = 297(129) Type = SHORT N =2 Этот тег используется для определения номера страницы в документе. Опре- делены 2 значения типа SHORT. Первое значение это номер страницы, второе значение — общее число страниц в документе. Отметим, что страницы не обязательно должны быть размещены в цифровой последовательности. Первая страница имеет номер 0 (ноль). Значение по умолчанию отсутствует.
3. Поля 463 XPosition Tag = 286(11E) Type = RATIONAL Смещение X левой части изображения по отношению к левой части страницы в поле ResoluiionUniis. Значение по умолчанию отсутствует. Смотрите также поле YPosition. YPosition Tag = 287(11F) Type = RATIONAL Смещение Y вершины изображения по отношению к вершине страницы в поле ResolutionUnits. В системе координат формата TIFF положительное направле- ние Y вниз от вершины изображения, таким образом, значение YPosition всегда положительно. Значение по умолчанию отсутствует. Смотрите также поле XPosition. Не рекомендуемые для использования (устаревшие) поля Эти поля не рекомендуются для использования, за исключением локального использования. Они не используются при взаимообмене изображений. Они либо заменяются другими полями, либо просто являются бесполезными. Они могут быть полностью удалены из спецификации будущих версий. CellLength Tag = 265(109) Type = SHORT N = 1 В 1-битовых образцах длина матрицы псевдотонирования/полутонирования. Принято, что поле Thresholding = 2. Это поле наряду с полями CellWidth и Thresholding очень проблематично, так как оно не может быть безопасно ис- пользовано для обратного получения полутонового изображения из псевдотони- рованного изображения, составленного из черных и белых элементов изображе- ния, что было единственной причиной его применения. Единственно верный путь — это не пользоваться этими полями, а напи- сать сложные программы, которые смогут манипулировать углами экрана, ко- торые не являются произведением числа 45 градусов. Итак, мы не рекомендуем преобразовывать полутоновые или псевдотониро- ванные данные в шкалу серого цвета. Эти данные требуют аккуратного обра- щения, чтобы избежать деформаций, но это можно сделать. Если вам необхо- димы изображения с непрерывной серой шкалой, получите их прямо от сканера или из какого-нибудь другого устройства. Значение по умолчанию отсутствует. Смотрите также поле Thresholding.
464 Приложение 2 CellWidth Tag = 264(108) Type = SHORT N = 1 В 1-битовых образцах ширина матрицы псевдотонирования/полутонирования. Значение по умолчанию отсутствует. Смотрите также поле Thresholding и комментарии к полю CellLength. FillOrder Tag = 266 (10А) Type = SHORT N =1 Порядок значений данных в пределах байта. 1 = Биты старшего порядка в байте заполняются первыми, т. е. значения упо- рядочены от старшего к младшему биту внутри байта. 2 = Биты младшего порядка заполняются первыми. Так как выражен интерес к битам младшего порядка и автор может легко изменить порядок бита (используя 256-байтовую таблицу преобразований), мы рекомендуем, чтобы значение FillOrder =* 2 только для приватного (не взаимообменного) приме- нения. Значение по умолчанию равно FillOrder = 1. FreeByteCounts Tag = 289(121) Type = LONG Для каждого «свободного блока» в файле число байтов в блоке. Читатели TIFF могут не учитывать поля FreeOffsets и FreeByteCounts. Поля FreeOffsets и FreeByteCounts не имеют отношения к изменениям логического адресного про- странства файла. Так как эта информация может быть получена с помощью сканирования IFD, поля Strip Offsets. StripByteCounts, FreeOffsets и FreeByteCounts не нужны. Неясно, что произойдет, если поля FreeOffsets и FreeByteCounts будут суще- ствовать в более чем одном IFD. Смотрите также поле FreeOffsets. FreeOffsets Tag = 288(120) Type = LONG Для каждого «свободного блока» в файле его байтовое смещение. Смотрите также поле FreeByteCounts. Max Sample Value Tag = 281(119) Type = SHORT N = SamplePerPixel
3. Поля 465 Максимально используемое значение образца. Например, если образец состо- ит из 6-битовых данных, выравненных по младшему порядку в 8-битовые бай- ты, то значение поля MaxSampleValue будет не более 63. Это поле не исполь- зуется для изменения вида изображения при выведении его на экран. Значения этого поля не оказывают влияния на толкование другого поля. Его можно ис- пользовать только для статистических целей. Значение по умолчанию равно 2**(BitsPerSample)-l. MinSample Value Tag = 280(118) Type = SHORT N = SamplesPerPixel Минимально используемое значение образца. Это поле не используется для изменения вида изображения при выведении его на экран. Смотрите коммен- тарии к полю MaxSampleValue. Значение по умолчанию равно 0. SubfileType Tag = 255(FF) Type = SHORT N =1 Общая индикация типа данных, содержащихся в этом подфайле. В настоящее время значения определены следующим образом: 1 = данные изображения полного разрешения — необходимыми являются поля ImageWidth, ImageLength и StripOffsets. 2 = данные изображения с уменьшенным разрешением — необходимыми явля- ются поля ImageWidth, ImageLength и StripOffsets. Далее принимается, что изображение с уменьшенным разрешением является уменьшенной версией полного содержания, соответствующего данным полного разрешения. 3 = единственная страница в многостраничном изображении (смотрите описа- ние тега поля PageNumber. Отметим, что некоторые типы изображений могут быть найдены в TIFF- файле с подфайлом, описанным своей собственной UFD. Значение по умолчанию отсутствует. Использование этих полей не рекомендуется. Авторы программ вместо это- го должны использовать новые типы полей или более общее поле NewSubfileType. Orientation Tag = 274(112) Type = SHORT N =1 1 = Нулевой ряд представляет вершину изображения, а нулевая колонка пред- ставляет левую часть изображения. 30-3
466 Приложение 2 2 = Нулевой ряд представляет вершину изображения, а нулевая колонка пред- ставляет правую часть изображения. 3 = Нулевой ряд представляет нижнюю часть изображения, а нулевая колонка представляет правую часть изображения. 4 = Нулевой ряд представляет нижнюю часть изображения, а нулевая колонка представляет левую часть изображения. 5 = Нулевой ряд представляет левую часть изображения, а нулевая колонка представляет вершину изображения. 6 = Нулевой ряд представляет правую часть изображения, а нулевая колонка представляет вершину изображения. 7 = Нулевой ряд представляет правую часть изображения, а нулевая колонка представляет нижнюю часть изображения. 8 = Нулевой ряд представляет левую часть изображения, а нулевая колонка представляет нижнюю часть изображения. Значение по умолчанию равно 1. Это поле рекомендуется только для частного (не взаимообменного) исполь- зования. Для большинства читателей слишком дорого осуществлять вращение изо- бражения при импортировании и печатании, и пользователи большинства на- стольных типографий не ожидают того, что файл, импортированный про- граммой, будет постоянно изменяться любым образом. Thresholding Tag = 263(107) Type = SHORT N = 1 1 = двухуровневое сканирование наброска карандашом. Поле BitsPerSample должно быть равно 1. 2 = псевдотонированное сканирование обычно полутоновых данных, таких, как фотография. Поле BitsPerSample должно быть равно 1. 3 = Рассеянная ошибка. Значение по умолчанию равно Thresholding = 1. Смотрите также поля Cell- Width и CellLength. 4 . Приватные поля Если организация желает хранить информацию, которая является значимой только для нее, в TIFF-файле используются теги с номером 32768 и выше. По вашей просьбе администратор обязан назначить и зарегистрировать блок при- ватных тегов для организации во избежание возможных конфликтов с другими организациями. Обычно теги размещаются в блоке по пять штук. Если этого недостаточно, то можно попросить еще. Вы можете не говорить админи- стратору TIFF или кому-либо другому об их использовании.
5. Некоторые вопросы формата файла изображения 467 Приватные значения перечисляемого типа могут быть размещены таким же образом. Например, вы хотите поэкспериментировать с новой схемой сжатия в TIFF. Константы перечисляемого типа с номером 32768 и выше используются для приватного использования. По вашей просьбе администратор обязан раз- местить и зарегистрировать блок значений перечисляемого типа для отдель- ного поля (например, Compression) во избежание конфликтов. Теги и значе- ния, которые размещаются в диапазоне приватных номеров, могут быть вклю- чены в будущую версию спецификации. В спецификации TIFF имеются такие примеры. Не выбирайте свои собственные номера тегов. Если вы так сделаете, то через некоторое время у вас могут возникнуть серьезные проблемы. 5 . Некоторые вопросы формата файла изображения Для устранения причин отказа от покупки данного продукта, некоторые про- граммы сканирования и редактирования изображения заваливают пользовате- лей невероятным числом опций в команде «Save As...» Необходимо избавиться от большинства из них. Например, одна опция TIFF может оказаться удо- влетворительной, так как большинство читателей поддерживают 3 схемы сжатия. Гибкость формата TIFF, включая приватный тег и возможности поддержки редактирования изображения, не кажется разумной причиной для записи файлов изображения с использованием собственных форматов. В то же время нельзя требовать от пользователя, чтобы он знал формат файла, который должен быть считан программой. TIFF-файлы так же, как и большинство других форматов файлов, содержат достаточно информации, чтобы дать возможность программе автоматически отличать один тип фай- ла от другого. 6 . Дополнительная информация Aldus Developersf Desk является в настоящее время «администратором» форма- та TIFF. Поддерживайте контакт с ним для получения образцов TIFF-файлов, фрагментов исходных кодов и перечня возможностей, в настоящее время под- держиваемых в программных продуктах Aldus. Различные виды помощи, связанные с форматом TIFF, оказываются Micro- soft’s Windows Developers Toolkit для разработчиков программ Windows. И на- конец, продавцы сканеров также осуществляют сервисное обслуживание TIFF, например, помогают распространять спецификацию TIFF и отвечают на раз- личные вопросы, касающиеся формата TIFF. Обращайтесь к соответствующему изготовителю продукта или в обслуживающую группу поддержки разработчика. Дополнение А. Логическое обоснование структуры тега Формат файла определяется с помощью обеих форм (структур) и содержания. Содержание TIFF состоит из определения индивидуальных полей. Это как раз то содержание, в котором мы заинтересованы. Структура объясняет нам, как 30*
468 Приложение 2 найти нужные нам поля. Тем не менее структура заслуживает серьезного вни- мания по определенным причинам, которые не являются очевидными с первого взгляда. Так как описанная структура существенно отличается от других под- ходов, полезно рассмотреть ее логическое обоснование. Самой простой и наиболее прямолинейной структурой файла изображения является позиционный формат. В позиционном формате каждая ячейка данных определяет, что это данное означает. Например, поле «номера ряда» должно начинаться с байтового смещения, равного 30, в файле изображения. Этот подход является простым для осуществления и «идеальным» для ста- тических сред. Но если необходимо разместить значительное количество измене- ний, при этом появляются некоторые проблемы. Например, предположим, что поле должно быть заменено новым, более общим полем. Вы можете указать номер версии, чтобы отметить изменение. У нового программного обеспечения не должно быть проблем, связанных с работой со старыми данными, а старое программное обеспечение отвергает новые данные, даже то программное обеспе- чение, которое не принимает во внимание старое поле. С первого взгляда может показаться, что это просто небольшая неточность, но слишком частое разру- шение старого программного обеспечения, которое очень дорого, вызовет много недовольства среди заказчиков. Более того, этого можно избежать. Одной из возможностей является накопление «действительных» флаговых битов для ка- ждого поля. Тогда вам не нужно будет указывать номер версии, так как вы сможете поместить новое поле в таком месте, что не будут нарушены ни одно из старых полей. Старое программное обеспечение, которое не принимает во вни- мание старое поле, так или иначе продолжает функционировать. (От старого программного обеспечения, которое принимает во внимание старое поле, необхо- димо отказаться.) Другой часто возникающей проблемой является следующее: определение по- ля имеет смысл только тогда, когда другие поля имеют определенные значения. На практике это оказывается не очень серьезной проблемой. Однако из-за этого все становится более запутанным. Тем не менее мы отметим, что «действитель- ные» флаговые биты, описанные выше, помогают прояснить ситуацию. Программы записи поля используются в диагностических целях. Основной особенностью такой программы является то, что она не знает, что такое запись. Но она будет считаться приемлемой в том случае, когда программа сможет за- писывать данные ASCII в формат ASCII, целые данные — в целый формат и так далее без постоянного обучения программ новым полям. Таким образом, мы, возможно, добавим компонент «тип данных» в наши поля плюс информацию о том, как долго существует поле, и после этого наша программа записи может проходить поля, не зная, что эти поля «означают». Но заметим, что если мы добавляем еще один компонент к каждому полю, а именно тег, который сооб- щит о том, что означает это поле, то мы можем обойтись без «действительных» флаговых битов и также избежать затрат на недействительные поля в файле. Простые программы создания изображения могут переписать некоторые поля, и они это делают. И, наконец, предостережение. Схема, основанная на теге, не гарантирует без- болезненный рост. Но она обеспечивает нас полезными методами для поддержа- ния этого процесса.
Дополнение В. Сжатие данных по схеме 2 469 Дополнение В. Сжатие данных по схеме 2 Этот документ описывает метод сжатия двухуровневых данных, который осно- ван на схеме факсимильного сжатия CCITT Group 3. Библиография 1. «Standardization of Group 3 facsimile apparatus for document transmission», Recommendation T.VII, Fascicle VII.3, Terminal Equipment and Protocols for Telematic Services, The International Telegraph and Telephone Consultative Committee (CCITT), Женева, 1985, c. 16-31. 2. «Facsimile Coding Schemes and Coding Control Functions for Group 4 Facsimile Apparatus», Recommendation T. 6, Volume VII, Fascicle VIL3, Terminal Equipment and Protocols for Telematic Services, The International Telegraph and Telephone Consultative Committee (CCITT), Женева, 1985, c. 40-48. Мы не думаем, что эти документы необходимы для выполнения Compres- sion = 2. Мы включили (в большинстве случаев стенографический отчет) в это приложение всю информацию, имеющую к этому отношение. Однако, если вы захотите упорядочить эти документы, вы можете обратиться в ANSI. Пишите по адресу: Sales, 1J30 Broadway, New York, N.Y., 10018. Попросите перечисленные выше публикации — они обе содержат Recommen- dation Т. 4 и Т. 6. Связь со спецификациями CCITT Спецификации CCITT Group 3 и Group 4 содержат информационные протоколы для отдельных классов устройств. Сами по себе они недостаточны для описания формата данных диска. К счастью, схемы кодирования могут легко быть при- способлены к различным средам. Далее следует такое приспособление. Большая часть языка скопирована прямо из спецификаций CCITT. Схема кодирования Линия (ряд) данных состоит из серий кодовых слов переменной длины. Каждое кодовое слово представляет собой длину пробега либо всех белых, либо всех чер- ных элементов. (Фактически может потребоваться более одного кодового слова, чтобы закодировать данный пробег описанным ниже способом.) Белые и черные пробеги чередуются. Чтобы обеспечить потребителю (декомпрессору) поддерж- ку цветовой синхронизации, все линии данных будут начинаться с установки кодового слова длиной пробега белых элементов. Если действительная линия сканирования начинается с черного пробега, длина пробега белого элемента бу- дет равна нулю. Длины пробегов белого или черного элемента определяются с помощью кодовых слов в табл. 1-3. Кодовые слова бывают двух типов: завершаю- щие и составляющие кодовые слова. Каждая длина пробега представлена нулем или словами составляющего кода, следующими точно за словом завершающего кода.
470 Приложение 2 Таблица 1. Завершающие коды Длина пробега белого элемента Кодовое слово Длина пробега черного элемента Кодовое слово 0 00110101 0 0000110111 1 000111 1 010 2 0111 2 11 3 1000 3 10 4 1011 4 011 5 1100 5 ООН 6 1110 6 0010 7 1111 7 00011 8 10011 8 000101 9 10100 9 000100 10 00111 10 0000100 11 01000 11 0000101 12 001000 12 0000111 13 000011 13 00000100 14 110100 14 00000111 15 110101 15 000011000 16 101010 16 0000010111 17 101011 17 0000011000 18 0100111 18 00000010000 19 0001100 19 00001100111 20 0001000 20 00001101000 21 0010111 21 00001101100 22 0000011 22 00000110111 23 0000100 23 100000101000 24 0101000 24 00000010111 25 0101011 25 00000011000 26 0010011 26 000011001010 27 0100100 27 000011001011 28 0011000 28 000011001100 29 00000010 29 000011001101 30 00000011 30 000001101000 31 00011010 31 000001101001 32 00011011 32 000001101010 33 00010010 33 000001101011 34 00010011 34 000011010010 35 00010100 35 000011010011 36 00010101 36 000011010100 37 00010110 37 000011010101 38 00010111 38 000011010110 39 00101000 39 000011010111 40 00101001 40 000001101100 41 00101010 41 000001101101 42 00101011 42 000011011010 43 00101100 43 000011011011 44 00101101 44 000001010100 45 00000100 45 000001010101 46 00000101 46 000001010110 47 00001010 47 000001010111 48 00001011 48 000001100100
Дополнение В. Сжатие данных по схеме 2 471 Таблица 1. (Продолжение) Длина пробега белого элемента Кодовое слово Длина пробега черного элемента Кодовое слово 49 01010010 49 000001100101 50 01010011 50 000001010010 51 01010100 51 000001010011 52 01010101 52 000000100100 53 00100100 53 000000101000 54 00100101 54 000000111000 55 01011000 55 000000100111 56 01011001 56 000000101000 57 01011010 57 000001011000 58 01011011 58 000001011001 59 01001010 59 000000101011 60 01001011 60 000000101100 61 00110010 61 000001011010 62 00110011 62 000001100110 63 00110100 63 000001100111 Таблица 2. Составляющие коды Длина пробега белого элемента Кодовое слово Длина пробега черного элемента Кодовое слово 64 11011 64 0000001111 128 10010 128 000011001000 192 010111 192 000011001001 256 0110111 256 000001011011 320 00110110 320 000000110011 384 00110111 384 000000110100 448 01100100 448 000000110101 512 01100101 512 0000001101100 576 01101000 576 0000001101101 640 01100111 640 0000001001010 704 011001100 704 0000001001011 768 011001101 768 0000001001100 832 011010010 832 0000001001101 896 011010011 896 0000001110010 960 011010100 960 0000001110011 1024 011010101 1024 0000001110100 1088 011010110 1088 0000001110101 1152 011010111 1152 0000001110110 1216 011011000 1216 0000001110111 1280 011011001 1280 0000001010010 1344 011011010 1344 0000001010011 1408 011011011 1408 0000001010100 1472 010011000 1472 0000001010101 1536 010011001 1536 0000001011010 1600 010011010 1600 0000001011011 1664 011000 1664 0000001100100 1728 010011011 1728 0000001100101 EOL 000000000001 EOL 000000000001
472 Приложение 2 Таблица 3. Дополнительные составляющие коды Длина пробега белого и черного элементов Слово составляющего кода 1792 00000001000 1856 00000001100 1920 00000001101 1984 000000010010 2048 000000010011 2112 000000010100 2176 000000010101 2240 000000010110 2304 000000010111 2368 000000011100 2432 000000011101 2496 000000011110 2560 000000011111 Длины пробега в диапазоне от 0 до 63 элементов изображения кодируются с их соответствующим словом завершающего кода. Отметим, что для длин пробега черного и белого элементов имеются различные перечни кодовых слов. Длины пробега в диапазоне от 64 до 2623 элементов изображения кодируются сначала составляющим кодовым словом, представляющим длину пробега, кото- рая не превышает требуемой длины. Затем следует завершающее кодовое слово, представляющее разность между требуемой длиной пробега и длиной пробега, представленной составляющим кодом. Длины пробега в диапазоне длин, больших или равных 2624 элементов изо- бражения, сначала кодируются составляющим кодом, равным 2560. Если остав- шаяся часть пробега (после первого составляющего кода, равного 2560) равна 2560 элементам изображения или больше, то происходит дополнительное коди- рование составляющего кода 2560 до тех пор, пока оставшаяся часть пробега не станет меньше 2560 элементов изображения. Затем оставшаяся часть пробега кодируется с помощью завершающего или составляющего кода плюс завершаю- щего кода в соответствии с упомянутым выше диапазоном. Считается ошибкой, если сумма длин пробега линии не равна значению поля ImageWidth. Новые ряды всегда начинаются на следующей границе байта. Кодовые слова EOL не используются. Заполненные биты не используются, за исключением неучтенных битов в конце ряда последнего байта. ВТС не ис- пользуется. Дополнение С. Сжатие данных (схема 32773) по методу PackBits В этом документе описана простая схема сжатия двухуровневых сканированных и рисованых типов файлов.
Дополнение С, Сжатие данных по методу PackBits 473 Обоснование Спецификация TIFF определяет число схем сжатия. Сжатие по типу 1 означает, что фактически сжатие отсутствует, кроме как фундаментальной упаковки эле- ментов изображения. Сжатие по типу 2, основанное на сжатии по методу CCITT ID, является мощным, но не простым для осуществления. Сжатие по типу 5 очень эффективно как для большинства двухуровневых изображений, так и для более глубоких изображений, таких, как изображения цветовой палитры, полу- тоновые изображения, но оно также не является простым для осуществления. Простой и наиболее эффективной альтернативой является сжатие по методу PackBits. Описание В различных случаях используются разные хорошие схемы сжатия. Мы произ- вольно выбрали схему сжатия Macintosh PackBits. Она ориентирована на байт, поэтому у нас не возникало проблем с упорядочением слов. Эта схема имеет наихудшее поведение (в лучшем случае 1 байт на каждый из 128 входных бай- тов). Пользователи Macintosh имеют программное обеспечение для использова- ния схем PackBits и UnPackBits, которые функционируют по вашему желанию, но гораздо лучше использовать свои собственные программы. Фрагмент псевдокода распаковки выглядит следующим образом: Повторяйте до тех пор, пока вы не получите число нераспакованных байтов, которое вы ожидаете; Читайте следующий исходный байт в п; Если значение п находится между 0 и 127 включительно, скопируйте бу- квально следующие п + 1 байты; Если значение п находится между —127 и —1 включительно, скопируйте сле- дующие байты — n + 1 раз; Если значение п равно 128, ПОП; Конец цикла В инвертированной программе лучше закодировать пробег двухбайтового по- вторения как пробег повторения, за исключением предшествующего и последу- ющего за символьным пробегом, в случае которого наилучшим будет соединение трех пробегов в один символьный пробег. Всегда кодируйте 3-байтовые повторе- ния как пробеги повторения. В этом и заключается алгоритм. Ниже приведены некоторые правила: • Каждый ряд должен быть упакован отдельно. Не переходите границ ряда. • Число байтов без сжатия в ряду определяется как (ImageWidth + 7)/8. Если распакованный битовый массив должен иметь четное число байтов в ряду, распакуйте буферы с упорядоченными словами. • Если пробег больше 128 байт, закодируйте остаток пробега как один или более дополнительно повторяющихся пробегов. Если данные распакованы по методу PackBits, считается, что имеется сжатие по типу 1 (сжатие отсутствует).
474 Приложение 2 Дополнение D Дополнение D исключено из этой спецификации. В более ранних вариантах оно содержало руководство для прохождения TIFF-файлов в Microsoft Windows Clipboard. Было решено, что это не самая лучшая идея для увеличения размеров сканированных изображений. Вместо этого поощряются программы употребле- ния механизмов, основанных на файлах, для обмена данных TIFF. Например, Aldus’ PageMaker ввел команду «File Place» для импорта TIFF- файлов. Дополнение Е« Перечень TIFF-тегов New Subfile Type Tag = 254 (FE) Type = LONG N = 1 Thresholding Tag = 263 (107) Type = SHORT N = 1 Subfile Type Tag = 255 (FF) Type = SHORT N = 1 CeHWidth Tag = 264(108) Type = SHORT N = 1 ImageWidth Tag = 256 (100) Type = SHORT or LONG N =1 CellLength Tag = 265 (109) Type = SHORT N = 1 ImageLength Tag = 257 (101) Type = SHORT or LONG N = 1 FiHOrder Tag = 266 (10A) Type = SHORT N =1 BitsPerSample Tag = 258 (102) Type = SHORT DocumentName Tag = 269 (10D) Type = ASCII N = SamplesPerPixel Compression Tag = 259 (103) Type = SHORT ImageDescription Tag = 270(10E) Type = ASCII N = 1 Photometriclnterpretation Tag = 262 (106) Type = SHORT Make Tag = 271 (10F) Type = ASCII N = 1
Дополнение Е. Перечень TIFF-тегов 475 Model Tag = 272 (110) Type = ASCII StripOffsets Tag = 273 (111) Type = SHORT or LONG N = StripsPerlmage для PlanarConfiguration равен 1. = SamplesPerPixel*StripsPerImage для PlanarConfiguration равен 2. Orientation Tag = 274 (112) Type = SHORT N =1 SamplesPerPixel Tag = 277 (115) Type = SHORT N =1 RowsPerStrip Tag = 278 (116) Type = SHORT or LONG N =1 StripByteCounts Tag = 279 (117) Type = LONG or SHORT N = StripsPerlmage для PlanarConfiguration равен 1. = SamplesPerPixel*StripsPerImage для PlanarConfiguration равен 2. MinSampleValue Tag = 280 (118) Type = SHORT N = SamplesPerPixel MaxSampleValue Tag = 281 (119) Type = SHORT N = SamplesPerPixel XResolution Tag = 282 (11A) Type = RATIONAL N =1 YResolution Tag = 283 (11B) Type = RATIONAL N =1 PlanarConfiguration Tag = 284 (11C) Type = SHORT N = 1 PageName Tag = 285 (11D) Type = ASCII XPosition Tag = 286 (HE) Type = RATIONAL YPosition Tag = 287 (HF) Type = RATIONAL FreeOffsets Tag = 288 (120) Type = LONG FreeByteCounts Tag = 289 (121) Type = LONG
476 Приложение 2 GrayResponseUnit Tag = 290 (122) Type = SHORT N = 1 Gray ResponseCurve Tag = 291 (123) Type = SHORT N = 2**BitsPerSample Group3Options Tag = 292 (124) Type = LONG N =1 Group4Options Tag = 293 (125) Type = LONG N = 1 ResolutionUnit Tag = 296 (128) Type = SHORT N = 1 PageNumber Tag = 297 (129) Type = SHORT N =2 Color ResponseCurves Tag = 301 (12D) Type = SHORT N = 3*(2**BitsPerSample) Software Tag = 305 (131) Type — ASCII Date Time Tag = 306 (132) Type = ASCII N = 20 Artist Tag = 315 (13B) Type = ASCII HostComputer Tag = 316 (13C) Type = ASCII Predictor Tag =317 (13D) Type = SHORT N = 1 WhitePoint Tag = 318 (13E) Type = RATIONAL N =2 Primary Chromaticities Tag = 319 (13F) Type = RATIONAL N =6 ColorMap Tag = 320 (Ц0) Type = SHORT N = 3*(2**BitsPerSample)
Дополнение F. Сжатие данных по типу LZW 477 Дополнение F. Сжатие данных (схема 5) по типу LZW В этом документе описаны схемы сжатия для растровых изображений. Библиография Terry A. Welch, «А Technique for High Performance Data Compression», IEEE Computer, vol.17 no.6 (июнь 1984). Описывается базовый алгоритм Lempel-Zive & Welch (LZW). Рассмотрен компрессор, который может быть встроен в контрол- лер диска или другое устройство и использован со всеми типами данных. Не содержит никакого особенного описания растровых изображений. Мы намере- ваемся дать достаточно информации в этом приложении, и поэтому эту статью можно не читать. Требования В настольных типографиях должна хорошо работать схема сжатия со следую- щими характеристиками: ♦ Хорошо работает с изображениями любой битовой глубины, включая изо- бражения с глубиной, большей 8 бит на элемент. ♦ Является эффективной: среднее соотношение сжатия по крайней мере рав- но 2:1 или лучше. Схема должна сама находить разумное решение в том случае, если в нее сбрасывается что-либо непонятное. ♦ Не зависит от небольших изменений между элементами изображения. Изображения с цветовой палитрой имеют тенденцию к резким изменениям в значениях индексов вследствие методик структурирования и псевдото- нирования. Эти резкие изменения, однако, стремятся к повторениям, и схема извлекает из этого пользу. ♦ Для изображений, созданных с помощью программ рисования, схема не зависит от данной ширины образца. В настоящее время наиболее часто встречаются образцы с 8 х 8 элементов изображения, но мы не можем утверждать, что эта ситуация не изменится. ♦ Большое быстродействие. Снятие сжатия с 100К-байтового полутонового изображения займет не более 5 с на компьютере 68020 или 386. Процесс сжатия может быть медленнее, но не более чем в 2 или 3 раза. ♦ Разумный уровень сложности выполнения. Например, мы хотим, чтобы квалифицированный инженер с опытом в обработке изображения выпол- нил какую-либо задачу не более чем в 2-недельный срок. Компилирующий код сжатия и распаковки будет не более чем 10 К. ♦ Не требуется программного обеспечения для работы с числами с плаваю- щей запятой и соответствующего оборудования. Ниже описывается алгоритм, основанный на методе LZW (Lempel-Ziv & Welch), который соответствует описанным выше требованиям. Удовлетворяя на- шим требованиям, метод сжатия LZW имеет следующие характеристики:
478 Приложение 2 ♦ Полностью обратимый. Вся информация сохраняется. Но, если из изо- бражения удаляется шум или информация с помощью сглаживания или установления в ноль некоторых битовых плоскостей младшего порядка, изображения сжимаются по методу LZW до более маленьких размеров. Таким образом, 5-, 6- или 7-битовые данные, преобразуясь в 8-битовые данные, сжимаются лучше, чем настоящие 8-битовые данные. Гладкие изображения также сжимаются лучше, чем изображения с шумом, и про- стые изображения сжимаются лучше, чем сложные. • В компьютерах 68082 или 386 может быть написана программа сжатия по методу LZW для сжатия от ЗОК до 80К байт в секунду в зависимости от характеристик изображения. Скорости распаковки по методу LZ W обычно составляют около 50К байт/с. ♦ Схема сжатия по методу LZW хорошо работает и для двухуровневых изо- бражений. Она всегда превосходит метод сжатия PackBits и, как правило, связывает сжатие по методу CCITT ID (модифицированный метод Хаф- фмена) на наших тестовых изображениях. Связывание сжатия по методу CCITT ID доказывает нам, что сжатие по методу LZW идет значительно быстрее, чем по методу CCITT ID, по крайней мере в наших разработках. ♦ Наша разработка написана на языке Си и компилирует около 2К байт в объектных кодах как для компрессоров, так и для декомпрессоров. ♦ Сжатие по методу LZW широко используется в архивирующих програм- мах. Алгоритм Каждая строка сжимается независимо от других строк. Мы убедительно реко- мендуем выбирать поле PowsPerStrip таким образом, чтобы каждая строка до сжатия содержала около 8К байт. Строки должны быть достаточно небольши- ми для того, чтобы сжатый и распакованный варианты строк могли полностью помещаться в памяти, даже в небольших устройствах. Но строки должны быть и достаточно большими для сохранения оптимальных пропорций сжатия. В основе алгоритма LZW лежит адресная или строчная таблица, которая устанавливает соответствие строк между входными знаками и кодами. TIFF ис- пользует коды различной длины, максимальная длина кода равна 12 бит. Для каждой строки существует отдельная строчная таблица, которая не нуждается в том, чтобы ее оставлять для процесса распаковки данных. Суть заключается в том, чтобы в процессе распаковки строилась точно такая же таблица, кото- рая строится при сжатии данных. Ниже на языке Си описана схема кодиро- вания. Initial izeStringTableO; WriteCode(ClearСоde); Г e пустая строка; for для каждого символа в полосе! К » Get Next Character () ; if Г + К в таблице строк! Г « Г + К; /♦ объединение строк */ }else!
Дополнение F. Сжатие данных по типу LZW 479 VriteCode(CodeFornString(F)); AddTableEntry(Г + К) ; Г - К; }/♦ конец цикла ♦/ WriteCode(СоdeFormString(Г)); WriteCode (EndOf Information); /* Г - это наша “префиксная строка'1 (здесь и далее везде) ♦/ Схема проста, хотя и достаточно смела для применения. Но мы все-таки по- стараемся объяснить некоторые проблемы до того, как перейти к процессу рас- паковки данных. «Символами», которые составляют строки после сжатия по методу LZW, являются байты, содержащие несжатые данные изображения (Compression^!), описанные TIFF. Например, если поле BitsPerSample равно 4, то каждый из 8- битовых символов LZW будет содержать два 4-битовых элемента изображения. Если поле BitsPerSample равно 16, то каждый 16-битовый элемент изображе- ния будет перекрывать два 8-битовых символа LZW. (Можно также исполь- зовать версию сжатия по LZW, в которой глубина символа LZW равна зна- чению поля BitsPerSample, как было описано в Draft 2 of Revision 5.0. Но в этом случае мы сталкиваемся с большими трудностями. Если значение поля BitsPerSample больше 11, мы не можем использовать 12-битовые максималь- ные коды, и по этой причине результирующая таблица LZW неприемлемо ве- лика. К счастью, благодаря тому, что LZW может приспосабливаться, мы не имеем больших потерь в соотношениях сжатия при комбинировании несколь- ких элементов изображения в один байт перед сжатием. Например, наши 4- битовые изображения сжаты на 3% меньше, а 1-битовые изображения сжаты на 5% больше. Легче описать компрессор LZW, который использует одинако- вую глубину символов, чем компрессор, который манипулирует различными глубинами.) Теперь мы перейдем к рассмотрению некоторых подпрограмм и различных указателей в нашем псевдокоде. Функция InitializeStringTable () инициирует строчную таблицу для того, что- бы она содержала все возможные строки единственного символа. Их число равно 256, пронумерованы они от 0 до 255, так как нашими символами являются байты. Функция WriteCode () записывает код в выходной поток. Первым записан код очистки, который определен как код #256. Функция GetNextCharacter () находит следующее значение символа из вход- ного потока. Это будет число от 0 до 255, так как нашими символами являются байты. Знаки «4-» означают соединение строк. Функция AddTableEntry () добавляет вход таблицы. Функция InitializeString- Table () складывает 256 входов в нашу таблицу. Каждый вход состоит из строки единственного символа, и они объединяются в значение кода, который в нашей программе идентичен самому символу. То есть нулевой вход нашей таблицы со-
480 Приложение 2 стоит из строки <0>, которая соответствует значению кода < 0 >, первый вход таблицы состоит из строки <1>, что соответствует значению кода < 1 >, .. и 255 вход таблицы состоит из < 255 > строк с соответствующим кодовым зна- чением < 255 >. Таким образом, первый вход, который мы добавляем в нашу строчную таблицу, будет иметь значение 256, правильно? Не совсем, так как мы сохраним код #256 для специального кода «очистки», а код #257 для специ- ального кода «EndOflnformation», который мы запишем в конце строки. Таким образом, первый вход множественного символа, добавленного к строчной табли- це, будет при значении 258. Например, у нас есть входные данные, которые выглядят следующим обра- зом: Элемент изображения 0: <7> Элемент изображения 1: <7> Элемент изображения 2: <7> Элемент изображения 3: <8> Элемент изображения 4: <8> Элемент изображения 5: <7> Элемент изображения 6: <7> Элемент изображения 7: <6> Элемент изображения 8: <6> Сначала мы считываем элемент 0 в К. ГК будет затем просто <7>, так как Г — это пустая строка в этой точке. Проверим, имеется ли уже строка <7> в строчной таблице? Конечно, имеется, так как все строки единственного симво- ла были помещены в таблицу с помощью функции InitializestringTable (). Итак, установим, что Г равно <7> и пойдем в верх цикла. Считаем элемент 1 в К. Проверим, есть ли значение ГК (<7><7>) в строчной таблице? Нет, поэтому нам необходимо будет проделать некоторую работу. Мы записываем код, свя- занный с Г в выход (записываем <7> в выход) и добавляем ГК (<7><7>) в таблицу как вход 258. Поместим К (<7>) в Г. Отметим, что хотя мы добавили строку, состоящую из элементов 1 и 2 в таблицу, мы снова используем элемент 1 как начало следующей строки. Возвращаемся назад к началу цикла. Мы считываем элемент изображения 2 в К. Проверим, есть ли значение ГК (<7><7>) в строчной таблице? Такое значение существует (мы его только что добавили), поэтому просто записываем К в Г, так что Г теперь равно <7><7>. Возвращаемся назад к началу цикла. Мы считываем элемент изображения 3 в К. Проверим, есть ли значение ГК (<7><7><8>) в строчной таблице? Нет, тогда мы записываем код, связанный с Г (<258>), в выход и добавляем ГК в таблицу как вход 259. Поместим К (<8>) в Г. Возвращаемся назад к началу цикла. Мы считываем элемент изображения 4 в К. Проверим, есть ли значение ГК (<8><8>) в строчной таблице? Нет, тогда запишем код, связанный с Г (<8>), в выход и добавим ГК в таблицу как вход 260. Поместим К (<8>) в Г. Продолжая, мы получим следующие результаты:
Дополнение F. Сжатие данных по типу LZW 481 После чтения: Мы записали в выход: И добавили вход таблицы: Элемент изображения 0 Элемент изображения 1 <7> 258: <7><7> Элемент изображения 2 Элемент изображения 3 <258> 259: <7><7><8> Элемент изображения 4 <8> 260: <8><8> Элемент изображения 5 <8> 261: <8><7> Элемент изображения 6 Элемент изображения 7 <258> 262 : <7><7><6> Элемент изображения 8 <6> 263 : <6><6> Функция WriteCode () также требует некоторого объяснения. Выходной код < 7 >< 258 >< 8 >< 8 >< 258 >< 6 > ... в нашем случае будет записан так, что- бы были использованы все возможные биты. В самом начале мы можем исполь- зовать 9-битовые коды, так как наши новые входы строчной таблицы большее 255, но меньше 512. Но если мы добавим вход таблицы 512, то должны включить 10-битовые коды. Аналогично включаем 11-битовые коды при значении 1024 и 12-битовые коды при значении 2048. Мы должны себя произвольно ограничить до 12-битовых кодов, чтобы наша таблица имела не более 4096 входов. Если мы будем двигаться дальше, то таблица будет слишком большой. Что же случится, если мы выйдем за пределы нашей строчной таблицы? Вот тогда и появится упомянутый выше код очистки. Так как используются 4094 входа, то мы можем записать (12-битовый) код очистки. (Если мы будем это делать слишком долго, то декомпрессор будет интерпретировать код очистки как 13-битовый код.) В этом случае компрессор еще раз инициирует строчную таблицу и начинает снова записывать 9-битовые коды. Отметим, что значение Г никогда не остается пустым, ни во время записи ко- да, ни в процессе добавления входа таблицы. Значение Г содержит один символ. Не потеряйте его, когда записываете конец таблицы кода очистки. Вы можете его записать либо как 12-битовый код до записи кода очистки, либо после ко- да очистки как 9-битовый код. Распаковка в обоих случаях даст одинаковый результат. Для упрощения задачи декомпрессору нам потребуется, чтобы каждая стро- ка начиналась с кода Clear (очистки) и заканчивалась кодом EndOflnformation. Каждая строка, сжатая по методу LZW, должна начинаться на границе бай- та. У нее нет необходимости начинаться на границе слов. Коды сжатия по методу LZW хранятся в байтах от старшего до младшего порядка, т. е. функция FillOrder принята равной 1. Коды сжатия записаны не как слова, а как байты, и, таким образом, сжатые данные будут идентичны независимо от типа файла, «II» или «ММ». Отметим, что строчные таблицы LZW — это непрерывная запись строк. Они отражают характеристики данных. Декодирование по методу LZW Процедура распаковки данных представляется немного более сложной: while((Code=GetNextCode())!=EoiCode){ 31 -3
482 Приложение 2 if(Code«=ClearCode) { InitializeTableO; Code«GetNext Code () ; if (Code»EoiCode) break; WriteString(StringFromCode(Code)); QldCode^Code; } /♦ конец процедуры ClearCode ♦/ else { if(IsInTable(Code)){ WriteString(StringFromCode(Code)); AddStringToTable(StringFromCode(01dCode)+FirstChar(StringFromCode(Code))); 01dCode=Code; } else { OutString=StringFromCode(OldCode)+ FirstChar(StringFromCode(OldCode)); WriteString(OutString); AddStringToTable(OutString); 01dCodesCode; } /* конец процедуры not-ClearCode ♦/ } /♦ конец цикла while ♦/ Функция GetNextCode () восстанавливает следующий код из закодированных по методу LZW данных. Необходимо поддерживать дорожку границ битов. Известно, что первым кодом, который получит эта функция, будет 9-битовый код. Мы добавляем вход таблицы каждый раз, когда получаем код, таким обра- зом функция GetNextCode () должна переключаться на 10-битовые коды, как только в таблицу будет помещена строка #511. Функция StringFromCode () получает строки, связанные с определенным ко- дом из строчной таблицы. Функция AddStringToTable () добавляет строки в строчной таблице. Знак «+», присоединяющий две части параметра к функции AddStringToTable (), обо- значает строчное соединение. Функция StringFromCode () имеет вид строки, соединенной с данным кодом. Функция WriteString () добавляет строку в выходной поток. Если значение поля SamplesPerPixel больше 1 До сих пор мы описывали схему сжатия, предполагая, что значение поля SamplesPerPixel всегда равно 1, как это было в случае цветовой палитры или полутоновых изображений. А что же будет с данными изображения RGB? Те- стирование наших изображений показывает, что соотношение сжатия по методу LZW в общих чертах подходит к изображениям RGB, независимо от того, чему равно значение поля Planar Configuration, 1 или 2. Таким образом, вы можете использовать любую конфигурацию и цри этом сжать байты в строку. Мы предупреждали, что соотношения сжатий для RGB-изображений очень малы: приблизительно от 1,1 до 1 и от 1,5 до 1 в зависимости от изображения.
Дополнение F. Сжатие данных по типу LZW 483 Потребители делают все для снятия шума с изображений. Предварительное те- стирование показывает, что значительно лучшие соотношения сжатий возможны при наименьшем шуме в изображениях. Даже обнуление двух наименее важных битовых плоскостей может оказаться эффективным, несмотря на малое или не- значительное повреждение изображения. Выполнение Возможно, наиболее важными решениями в выполнении компрессии и деком- прессии по методу LZW является точное построение строчной таблицы и метод, используемый для определения, всегда ли в таблице есть строка. Было предло- жено хеширование как полезный метод для компрессии. Мы выбрали подход, основанный на дереве, который дает хорошие результаты. Декомпрессия действительно является более простой и быстрой, и поэтому для нее не нужны исследования — строки могут быть получены прямо из зна- чений кода. Производительность Многие не понимают, что производительность любой схемы сжатия очень сильно зависит от типа применяемых данных. Схема, которая хорошо функционирует с одними данными, может плохо работать с другими. Но так как мы не хотим предлагать очень большое число схем сжатия, нам нужна такая схема сжатия, которая хорошо функционирует с разнообразными изображениями. Такой схемой является схема сжатия по методу LZW. Эта схема не всегда дает оптимальное соотношение сжатия, но благодаря своей адаптивной природе и относительной простоте она является наиболее подходящей схемой. Экспериментально было установлено, что соотношения сжатия будут нахо- диться в диапазоне от 1,5 и 3,0 до 1 при сжатии по методу LZW, без потерь данных у полутоновых изображений. Если мы обнулим наименее важные одну или две битовые плоскости 8-битовых данных, то могут быть достигнуты лучшие соотношения сжатия. Эти битовые плоскости часто состоят из шума, поэтому не будут замечены потери качества изображения. Изображения цветовой палитры, созданные с помощью программы рисования, сжимаются гораздо лучше, чем полутоновые изображения, так как прорисованные изображения оказываются более повторяемыми. Можно достичь соотношения сжатия 10 к 1 или даже еще лучшего при использовании сжатия по методу LZW изображений цветовой па- литры. Для сравнения используемая в формате TIFF для черных и белых двухуров- невых изображений схема сжатия PackBits плохо работает с цветными прорисо- ванными изображениями, гораздо хуже, чем с полутоновыми и цветными изобра- жениями. Среднее соотношение здесь составляет 1,2 к 1 для 4-битовых изображе- ний, а для 8-битовых изображений — еще меньшее соотношение. Схему сжатия по методу CCITT ID можно использовать для полутоновых изображений, сжи- мая отдельно каждую строку. Без сомнения, можно достичь некоторого сжатия, но кажется маловероятным, что схема, основанная на фиксированной таблице, которая оптимизируется для коротких пробегов черных элементов, разделенных 31*
484 Приложение 2 более длинными пробегами белых элементов, будет хорошим вариантом для лю- бой из битовых плоскостей. Эта схема функционировала бы хорошо с битовыми плоскостями старшего порядка (но это уже делает более простая схема, такая как PackBits) и плохо бы работала с битовыми плоскостями младшего порядка. Мы думаем, что соотношения сжатия были бы не очень хорошими и, кроме того, процесс был бы очень медленным. Расщепление элементов изображения на бито- вые плоскости и объединение их довольно дорого, кодирование также достаточно медленно в программном обеспечении. Другой Подход использует ступень 2D, следующую за кодированием разно- стей, применяя таблицу кодов разной длины. Эта схема хорошо работает со мно- гими 8-битовыми полутоновыми изображениями, и, возможно, является более простой в выполнении, чем метод LZW. Но она обладает определенными недо- статками, когда речь идет о большом числе изображений. Во-первых, эта схема не адаптивна, что создает большую разность, когда сжимаемые, например, 8- битовые изображения по одному из стандартных методов становятся резкими. При сжатии такие изображения стремятся стать больше, а не меньше. Другим ее недостатком является то, что она плохо работает с широким диапазоном бито- вых глубин. Чтобы быть эффективной, таблица встроенных кодов должна быть оптимизирована для определения битовой глубины. Наконец, мы должны упомянуть о «потерянных» схемах сжатия. В этой обла- сти выполнено много исследований. В основном при использовании этих методов получаются гораздо более высокие соотношения сжатия, чем можно достичь с помощью полностью обратимых или сохраняющих информацию методов сжатия изображений, таких, как например, метод сжатия PackBits и LZW. Эти мето- ды имеют определенные недостатки. Многие из потерянных методов являются очень дорогими для расчета. Другие являются настолько сложными, что боль- шинство производителей программного обеспечения для микрокомпьютеров не позволяют себе ни затрат на выполнение этих методов, ни увеличения кода про- граммы. Тем не менее производители готовы жертвовать качеством изображе- ний, при условии что они подойдут для опубликования на микроЭВМ. Несмотря на эти трудности, мы думаем, что придет время, когда потерянные схемы сжа- тия для полных цветных изображений будут стандартизированы и их мсйкно будет использовать в различных целях. Международная Организация Стандар- тов ISO/IEC/JTC1/SC2/W68 при сотрудничестве с CCITT Group VIII усиленно работает над схемой, которая будет соответствовать нашим требованиям. Мы думаем, что следующее пересмотренное издание TIFF будет содержать эту схе- му, так как она удовлетворяет требованиям настольных типографий и многим другим требованиям. Это еще раз подтвердит, а не заменит сжатие по методу LZW как утвержденную схему сжатия TIFF. Метод LZW скорее всего останет- ся основной схемой сжатия для изображений цветовой палитры и, возможно, 4-битовых полутоновых изображений и может соревноваться с методом CCITT ID и PackBits для двухуровневых изображений. Будущее развитие схемы сжатия по методу LZW Некоторые изображения лучше подвергаются сжатию при использовании схемы LZW, если сначала каждое значение элемента в изображении заменить на раз-
Дополнение G. Классы TIFF 485 ность между значением данного элемента и значением предшествующего элемен- та изображения. Представление этой разности в двух размерах очень «помогает» некоторым изображениям. Однако многие изображения не сжимаются даже по- сле этой обработки и для большего числа изображений соотношения сжатия остаются плохими. Поэтому мы не повторяем эту предварительную обработку как неотъемлемую часть схемы сжатия по методу LZW формата TIFF. Однако такая «предсказанная» стадия возможна, так как является эффек- тивной для широкого диапазона изображений. Бели будут найдены еще такие схемы, то они будут включены в следующий выпуск TIFF. Если это произойдет, то в будущей версии формата TIFF будет определено новое значение для тега «Predictor». Поэтому все читатели TIFF, которые интересуются файлами схемы сжатия по методу LZW, должны обращать внимание на тег Predictor. Если его значение равно 1, что бывает в случае значений по умолчанию, распаковка по методу LZW может протекать нормально. Если значение этого тега не равно 1 и читатели не одобрили предсказанную схему, то от нее необходимо отказаться. Благодарности В этом приложении представлена исходная рекомендация для использования схемы сжатия по методу LZW. Использование кода ClearCode как метода мани- пулирования переполнением было заимствовано из схемы сжатия, используемой Graphic Interchange Format (GIF), форматом файла, используемым CompuServe, адаптированным к методу LZW. Джофф Морган и Эрик Робинзон из фирмы Aldus также много сделали для разработки схемы сжатия по методу LZW. Дополнение G. Классы TIFF Логическое обоснование TIFF был разработан для того, чтобы облегчить работу производителям скане- ров, разработчикам программного обеспечения настольных типографий и поль- зователям их продукции путем уменьшения разрастания форматов правильно сканированных изображений. В этом отношении формат TIFF превзошел все на- ши ожидания. Но мы также думали, что TIFF будет интересен только неболь- шому числу производителей сканеров (их приблизительно столько же, сколько и было в 1985 г.) и разработчикам программного обеспечения настольных типо- графий. Это обернулось для нас огромной недооценкой. Причиной такого успеха явилось то, что TIFF представляет собой мощное и гибкое средство, оставаясь в то же время и простым. Осуществляется значитель- ное число попыток для манипуляций с различными вариантами, определенны- ми в этой спецификации (возможно, никакая программа не закончит работу), и в настоящее время единственным путем, в котором вы можете быть уверены, является ваша способность импортировать изображение формата TIFF, так как в настоящее время имеется очень много программ создания изображения. Итак, здесь содержится попытка перевести в более нужное русло некоторую способность TIFF к гибкости, используя все то, что мы изучили за последние два года. Результатом этого является возможность программистов легко записывать
486 Приложение 2 файлы, которые будут успешно прочитаны различными программами и читате- ли смогут узнать, когда они закончат добавление признаков TIFF. Составление классов формата TIFF неизбежно приводит к потере способности TIFF к ада- птации. Если установим определенные требования к классу TIFF, мы никогда не сможем добавить новых требований, так как старое программное обеспечение их не воспримет. (Самое лучшее, что при этом можно сделать, — установить новые классы TIFF. Но проблема заключается в том, что у нас скоро будет слишком много классов TIFF.) Таким образом, мы должны поверить, что мы знаем, что необходимо делать при установке этих классов. Если мы об этом не узнаем, лю- бая из ошибок будет слишком дорогой. Обзор Определены 4 класса для формата TIFF. ♦ Класс В для двухуровневых (1-битовых) изображений. ♦ Класс G для полутоновых изображений. ♦ Класс Р для изображений цветовой палитры. ♦ Класс R для полных цветных изображений RGB. Обычно при ссылке на классы формата TIFF принято их называть «TIFF В», «TIFF G», «TIFF Р» и «TIFF R». Если речь будет идти обо всех четырех классах, то они будут обозначаться как «TIFF X». Замечание для людей, пользующихся факсом: если вы заинтересованы в фак- симильной связи TIFF F, то, пожалуйста, соберитесь все вместе и решите, что будет находиться в файлах TIFF класса F. Сообщите нам об этом и если вам понадобится наша помощь, мы всегда рады вам помочь. Пришлите нам ваши решения, чтобы мы могли включить вашу информацию в будущие версии специ- фикации. Требования к оперативной памяти В этой части приложения описываются требования, общие для всех изображений TIFF X. Общие требования Требования ко всем файлам TIFF X. Читатели TIFF X могут выбрать из этого описания то, что им нужно, хотя мы рекомендуем научиться обращаться со всеми классами формата TIFF. Пожалуйста, следуйте нашим рекомендаци- ям. Возможно, в будущем будут определены новые и даже более простые TIFF- классы, которые будут содержать только рекомендуемые свойства. Но сначала вам все-таки необходимо прочитать по крайней мере первые три части основной спецификации, чтобы полностью понять последующй материал. Значения по умолчанию. Программисты TIFF X могут, но не обязаны записать поле, которое имеет значение по умолчанию, если значение по умолча- нию является желательным. Читатели должны уметь манипулировать в любой ситуации.
Дополнение G. Классы TIFF 487 Другие поля. Читатели TIFF X должны быть готовы к появлению не только требуемых полей в TIFF Х-файлах. Программистам TIFF X разрешено описать поля, такие, как Make, Model, DateTime и так далее, и читатели TIFF могут извлечь из таких полей пользу, если эти поля существуют. Читатели TIFF X, однако, не должны отказываться от считывания файла, если таких полей не существует. Порядок байтов «ММ» и «II». Читатели TIFF X должны уметь обращать- ся с обоими порядками байтов. Программисты TIFF X могут сделать то, что, на наш взгляд, является наиболее удобным или эффективным. Изображения про- ходят границу в компьютере IBM PC/Macintosh (как и в других компьютерах) с удивительно высокой частотой. Можно рекомендовать программистам исполь- зовать тот же байтовый порядок, но предварительные данные показывают, что при этом возникнут проблемы для изображений, больших 8 бит. Обратные байты при сканировании могут настолько замедлять процесс сканирования, что вызо- вут остановку механизма сканирования, а это создаст проблемы качества изо- бражения. Множественные подфайлы. Читатели TIFF X должны быть готовы к мно- жественным изображениям (т. е. подфайлам) в TIFF-файле, хотя им и не тре- буется ничего делать с любым изображением, следующим за первым. Програм- мисты TIFF X должны записать длинное слово нулями после последнего IFD (это указывает на то, что этот IFD является последним), как показано в струк- туре TIFF. Если программисты записывают множественные подфайлы, первым должно идти изображение с полным разрешением. Последующие подызображе- ния, такие как изображения с уменьшенным разрешением и прозрачные маски, могут находиться в любом порядке в TIFF-файле. Если читатель хочет исполь- зовать такие подызображения, то нужно просканировать IFD перед тем, как начать работу. Редакторы TIFF X. Редакторы, в которых программы модифицируют TIFF-файлы предъявляют дополнительные требования. TIFF-редакторы долж- ны особенно осторожно обращаться с подфайлами. Если TIFF-редактор редак- тирует подфайл с полным разрешением, но не изменяет сопровождающий под- файл с уменьшенным разрешением, то читатель, который пользуется подфайлом с уменьшенным разрешением, будет выводить на экран неправильные изображе- ния. Таким образом, редакторы TIFF должны либо создавать новый подфайл с уменьшенным разрешением, когда они изменяют подфайл с полным разреше- нием, либо просто исключить любые подфайлы, которые не готовы для работы. Аналогичная ситуация возникает и с самими полями. TIFF Х-редактор отвечает только за обязательные поля. В частности, не обязательно и, возможно, даже опасно для редактора копировать поля, которые он не понимает. Можно изме- нить файл таким образом, чтобы он был несовместим с незнакомыми полями. Обязательные поля MewSubfileType. Тип LONG. Рекомендуется, но не требуется. ImageWidth. Тип SHORT или LONG. (То есть принимаются оба типа дан- ных «SHORT» и «LONG» и читатель должен правильно манипулировать ими. Программисты TIFF могут использовать любой тип.) Читателям TIFF не тре- буется произвольно считывать большие файлы. Некоторые читатели могут от-
488 Приложение 2 казаться от этого, если все изображение не будет соответствовать имеющейся в распоряжении памяти. (В таких случаях читатель должен проинформировать пользователя о сути проблемы.) Другие, возможно, смогут управлять параме- тром ImageWidth большим чем 65535. Рекомендация: используйте тип LONG, так как разрешения, по-видимому, будут возрастать. ImageLength. Тип SHORT или LONG. Рекомендация: используйте тип LONG. RowsPerStrip. Тип SHORT или LONG. Читатели должны уметь манипули- ровать любым значением от 1 до 2**32-1. Однако некоторые читатели пытаются сразу прочитать целую строку в памяти, поэтому если целое изображение зани- мает одну строку, то программа может выйти за пределы памяти. Рекомендация 1: установите параметр SetRowsPerStrip таким образом, чтобы размер каждой строки был около 8К байтов. Сделайте это даже с несжатыми данными, так как это будет проще для программиста и для читателей. (Замечание. Очень широкие изображения с высоким разрешением могут иметь ряды, большие, чем 8К байт, в этом случае параметр RowsPerStrip будет равен 1, и строка будет больше 8К байт.) Рекомендация 2: используйте тип LONG. StripOffsets. Тип SHORT или LONG. Как уже объяснялось выше (в основной части), значение StripOffsets зависит от RowsPerStrip и ImageLength. Рекомендация: всегда используйте тип LONG. (Тип LONG, конечно, должен использоваться в том случае, если длина файла более 64К байт.) StripByteCounts. Тип SHORT или LONG. Многие существующие изобра- жения формата TIFF не содержат параметра StripByteCounts, так как строго говоря, они не нужны. Читатель TIFF сам сможет записать точный размер сжа- той строки. Но это значительно усложнит все, таким образом, нам требуется StripByteCounts в TIFF Х-файлах. Рекомендация: используйте тип SHORT, так как предполагается, что строки очень велики. XResolution, YResolution. Тип RATIONAL. Запомните, что разрешения X и Y могут быть неравными. TIFF Х-читатель должен уметь манипулировать в этом случае. Редакторы элемента изображения TIFF X обычно не думают о раз- решении, но существуют программы, такие, как программа размещения страниц. ResolutionUnit. Тип SHORT. Читатели TIFF X должны уметь управлять всеми тремя значениями ResolutionUnit. TIFF В-класс для двухуровневых изображений Требования (дополнение к описанным выше) Для TIFF В файлов в дополнение к полям, обязательным для всех TIFF X- изображений (см. выше), также требуются следующие поля. SamplesPerPixel = 1. Тип SHORT. (Так как имеется значение по умолча- нию, поле может быть и не представлено. То же самое происходит и с другими обязательными полями TIFF X, которые имеют значение по умолчанию.) BitsPerSample = 1. Тип SHORT. Compression = 1, 2 (CCITT ID) или 32773 (PackBits). Тип SHORT. Читатели TIFF В должны уметь обращаться со всеми тремя схемами. Рекомен- дация: используйте PackBits. Это простая, быстрая, эффективная схема сжатия.
Дополнение G. Классы TIFF 489 Таблица 1. Пример TIFF В-изображения Смещение (hex) Название Значения (в основном hex) Header: 0000 Byte Order 4D4D 0002 Version 002A 0004 1st IFD pointer 00000014 IFD: 0014 Entry Count 000D 0016 N e wSubfileTy pe 00FE 0004 00000001 00000000 0022 ImageWidgth 0100 0004 00000001 000007D0 002Е ImageLength 0101 0004 00000001 00000BB8 003А Compression 0103 0003 00000001 80050000 0046 Photometricinterpretation 0106 0003 00000001 00010000 0052 StripOffset 0111 0004 000000BC 000000B6 005Е RowsPerStrip 0116 0004 00000001 00000010 006А StripByteCounts 0117 0003 000000BC 000003A6 0076 XResolution ОНА 0005 00000001 00000696 0082 YResolution 011B 0005 00000001 0000069E 008Е Software 0131 0002 0000000E 000006A6 009А DateTime 0132 0002 00000001 000006B6 00А6 Next IFD pointer 00000000 Поля, обозначенные тегами: 00В6 StripOffset OffsetO,Offsetl,... Offsetl87 03А6 StripByteCounts CountO,Coiin.tl,... Countl87 0696 XResolution 0000012C 00000001 069Е YResolution 0000012C 00000001 06А6 Software «PageMarker 3.0» 06В6 DateTime «1988:02:18 13:59:59» Данные изображения: 00000700 Compressed data for strip 10 хххххххх Compressed data for strip 179 хххххххх Compressed data for strip 53 хххххххх Compressed data for strip 160
490 Приложение 2 Метод CCITT более эффективен в некоторых случаях, в таких, как сканирова- ние страницы текста, но труден для выполнения и тестирования и довольно медленный. Кроме того, сканирование страницы 12-точечного текста не очень хорошо для издательских программ, если только данные изображения не пре- образутся в текст ASCII путем программного обеспечения OCR, которое не рас- сматривается в TIFF. Photometriclnterpretation = 0 или 1. Тип SHORT. Комментарии к примеру TIFF В 1. В нашем примере IFD начинается с позиции 14Н. Он может находиться в любом месте в файле до тех пор, пока его позиция имеет четное значение и больше или равно 8, так как заголовок TIFF имеет 8 байт и должен идти первым в TIFF-файле. 2. Имеется всего 188 полос, в каждой из которых 16 рядов. 3. В примере используются необязательные поля, такие, как DateTime. Чи- татели TIFF X должны осторожно пропускать эти поля, если они не хотят пользоваться этой информацией. Читатели TIFF X не должны требовать наличия этих полей. 4. В нашем примере содержатся сильно фрагментированные данные изобра- жения, строки в этом изображении находятся даже не по порядку. Де- ло заключается в том, что смещением строк нельзя пренебрегать. Нико- гда не допускайте, чтобы строка N+1 следовала за строкой N. В част- ности, нет необходимости, чтобы данные изображения следовали за IFD- информацией. Необходимо только следить за указателями, являются ли они указателями на IFD, на поле или на смещение строки. TIFF класс G для полутоновых изображений Требования (дополнение к описанным выше) SamplesPerPixel = 1. Тип SHORT. BitsPerSample = 4, 8. Тип SHORT. Кажется, что здесь имеется небольшое обоснование для работы с полутоновыми изображениями, более мелкими, чем 4- 7-битовые изображения, они легко могут хранится как 8-битовые изображения в течение того времени, когда вы сжимаете «неиспользованные» битовые плос- кости без штрафа. Мы можем поступать так же и с сжатием по методу LZW (Compression = 5). Compression = 1 или 5 (LZW). Тип SHORT. Рекомендации: используйте сжатие, равное 5, так как изображение без сжатия по методу LZW создаются очень быстро. Photometriclnterpretation = 0 или 1. Тип SHORT. Рекомендация: исполь- зуйте значение 1 для интерфейсов пользователей при регулировке яркости и контрастности.
Дополнение G. Классы TIFF 491 TIFF класс P для цветовой палитры Требования (дополнение к описанным выше) SamplesPerPixel = 1. Тип SHORT. Мы используем значение каждого эле- мента как индекс во все три цветовые таблицы в ColorMap. BitsPerSample = 1, 2, 3, 4, 5, 6, 7 или 8. Тип SHORT. Наиболее часто встречаются 1, 2, 3, 4 и 8, но только в тот промежуток времени, пока мы это делаем. Compression = 1 или 5. Тип SHORT. Photometricinterpretation = 3 (цветовая палитра). Тип SHORT. ColorMap. Тип SHORT. Отметим, что двухуровневые и полутоновые изо- бражения могут быть представлены как частные случае изображений цвето- вой палитры. Как только появится достаточное число программ, поддерживаю- щих изображения цветовой палитры, мы сможем отказаться от различий между двухуровневыми, полутоновыми изображениями и изображениями цветовой па- литры. TIFF класс R для полных цветных изображений RGB Требования (дополнение к описанным выше) SamplesPerPixel = 3. Тип SHORT. Каждый образец для красного, зеленого и синего. BitsPerSample = 8, 8, 8. Тип SHORT. Короткие образцы могут легко хра- ниться как 8-битовые образцы без штрафа, если данные сжаты по методу LZW. Но опыт показывает, что изображения, длиннее 8-битовых образцов, работают не хуже даже в таких требовательных программах, как издательские. PlanarConfiguration — 1 или 2. Тип SHORT. Рекомендация: используйте значение, равное 1. Compression = 1 или 5. Тип SHORT. Photometricinterpretation = 2 (RGB). Тип SHORT. Рекомендации Для TIFF R рекомендуются, но не являются обязательными новые (как в Revision 5.0) колориметрические информационные теги. Смотрите Дополнение Н. Согласование и интерфейс пользователя Программы, которые записывают TIFF Х-файлы, должны содержать «TIFF В» и/или «TIFF G» и/или «TIFF R» и/или «TIFF Р» и/или листы спецификации, если они могут записать соответствующие файлы TIFF X. Если ваши програм- мы записывают все четыре вида, вы можете записать это как «TIFF B,G,P,R». Конечно, термин «TIFF В», хороший для связи с другими производителями, не дает много информации пользователю. В этом случае лучше использовать фразу «Standard TIFF Black-and-White Scanned Images». Эта же терминология применяется к программам, которые считывают файлы TIFF Х-класса.
492 Приложение 2 Если ваша программа считывает большее число файлов, чем записывает, или наоборот, то необходимо объяснить это покупателю. Например, если ваша про- грамма читает файлы TIFF В и TIFF G, но записывает только файлы TIFF G, то вы должны записать это в лист спецификации. Дополнение Н. Колориметрическое описание изображения Chris Seas 210 Lake Street San Fransisco, CA 94118 June 4, 1988 Revised August 8, 1988 1. Введение Цель нашего исследования — точное воспроизведение цветного изображения с помощью различных устройств. Для этого требуются независимые методы изме- рения и стандарты сравнения. Колориметрия обеспечивает основу для решения этих проблем. Если изображение имеет полное колориметрическое описание, его можно тождественно воспроизвести на различных мониторах, используя разные способы, такие, как офсетная печать. Колориметрические данные задаются при создании или изменении изображения. Сканированное изображение имеет коло- риметрические данные, полученные из фильтров и световых источников сканера, а синтетическое изображение имеет колориметрические данные, соответствую- щие монитору, используемому при создании изображения. Эти данные исполь- зуются для отображения входного изображения на выходном устройстве. В разд. 2 помещен перечень различных организаций, занимающихся вопроса- ми стандартизации, и их работ, связанных с вопросами цветовых изображений. В разд. 3 описываются причины поиска тегов. Задачи воспроизведения изобра- жения рассмотрены в разд. 4. Колориметрические данные представлены в разд. 5-7. Теги описываются в разд. 8. В разд. 9 рассмотрены значения по умолчанию. Ограничения и некоторые другие вопросы рассмотрены в разд. 10. В разд. 11 приведены некоторые ссылки. 2. Колориметрические стандарты TIFF — общий стандарт для описания данных изображения. Для нас разумно изменить TIFF таким образом, чтобы он не соответствовал существующим про- мышленным и международным стандартам. Поэтому ниже мы опишем попытки различных организаций по стандартизации и выберем значения по умолчанию из их работ. CIE (Commission Internationale de I’Eclairage). Основой информации по колори- метрии является CIE 1931 Standard Observer [3]. Модели цветов рассмотрены в работах [1, 4], CIE 1931 XYZ — международный стандарт, принятый для всех отраслей промышленности, для определения цветов, a CIE xyY — хро- матическая диаграмма, связанная со значениями CIE 1931 XYZ.
Дополнение Н. Колориметрическое описание изображения 493 NTSC (National Television System Committee). Стандарт NTSC интересен в основном в историческом плане и благодаря его использованию для коди- ровки телевизионных сигналов. Производственные стандарты для мониторов существенно отличаются от колориметрической спецификации 1953 NTSC. SMPTE (Society of Motion Pictures and Television Engineers). Большинство работ NTSC было вопринято SMPTE. Эта организация имеет перечень стандартов, которые называются «Recommended Practicles» и применяются для различ- ных пленок и телевизионной продукции [5, 6]. ISO (International Standards Organization). Эта организация принимала уча- стие в создании цветовых стандартов, работая над приложением к «Office Document Architecture (ODA) and Interchange Format» [7]. ANSI (American National Standards Institute). ANSI — американский предста- витель ISO. 3» Цели При определении нужных нам тегов мы руководствуемся нашими исследова- ниями и разработками в технологии разделения цветов. С приведенной здесь информацией и данными элементов изображения RGB мы имеем всю необхо- димую нам информацию о высококачественном разделении цветов. Мы можем поддерживать колориметрическую информацию вне файла изображения. Но так как TIFF дает нам удобный механизм для объединения всей соответствующей информации в одном месте, теги определены для описания этой информации в TIFF файлах цвета. Цветное изображение, воспроизведенное с неправильной колориметрической информацией, отличается от исходного. Одно из наших первых тестовых изобра- жений содержит в себе обман зрения, в котором сканированное изображение с одним набором значений и цвета было перекрыто сверху другим набором зна- чений. При распечатке синий цвет превратился в фиолетовый. Использование неправильных таблиц цвета или белых точек также приводит к искажению изо- бражений. Для устранения этих ошибок необходимо, чтобы создатель изображе- ния снабдил его колориметрической информацией, наряду со значениями RGB [1, 2]. Основное назначение колориметрических данных — возможность преобразо- вания от главных значений и белой точки изображения к главным значениям и белой точке воспроизведенных данных. Гамма отражает градиент нелинейной передачи реальных данных. 4. Колориметрическое воспроизведение Выше уже говорилось, что соответствующие колориметрические данные изобра- жения могли быть тождественно воспроизведены с помощью двух различных калиброванных устройств. Под тождественностью мы подразумеваем колориме- трическую репродукцию [9]. Цветовые соответствия и яркость промасштабиро- ваны для соответствия диапазону яркости выходного устройства. Для этого нам необходимы цветовые координаты белой точки и главных значений. Абсолютная яркость является произвольной и от нее можно отказаться.
494 Приложение 2 5. Белая точка В спецификации TIFF 4.0 белая точка определена как Des- В этом приложении назначен отдельный тег для описания белой точки, a Des является логическим значением по умолчанию, с тех пор как стандартом является SMPTE [6]. Белая точка колориметрически определена из цветовой диаграммы CIExyY. Так как мониторы не могут различить Des, то часто сканированные изображения име- ют различные белые точки. Воспроизведенные изображения могут иметь произ- вольные белые точки. В графике D$o используется как стандартный видимый световой источник [8]. 6. Основные положения хроматичности В спецификации TIFF 4.0 основные цвета соответствовали спецификации NTSC. При широком разнообразии цветовых сканеров, мониторов и изображений TIFF нуждается в механизме точного описания хроматичности основных цветов. Мы используем стандарт SMPTE как значение хроматичности по умолчанию, так как стандартные мониторы в большей степени соответствуют стандартам SMPTE, а некоторые мониторы (Сопгас 6545) разработаны специально для спе- цификации SMPTE. Мы не используем хроматичность и белую точку стандарта NTSC, так как современные мониторы их не используют. Например, хроматичность основного цвета, используемого Sony Trinitron, от- личается от хроматичности, рекомендуемой SMPTE. Так как действительные мониторы отличаются от промышленных стандартов, хроматичность в основном описана в системе CIExyY. Это позволяет системе репродукции компенсировать эти различия. 7» Компоненты цвета Этот тег определяет три компоненты: одна — для красного, другая — для зеленого и третья — для синего цвета. Ширина каждого входа составля- ет 16 бит, что подразумевает тип SHORT. Минимум интенсивности Предста- влен 0, максимум — 65535. Например, черный цвет представлен 0,0,0, а белый цвет — 65535,65535,65535. Длина каждой компоненты — 2**BitsPerSample. Поле ColorResponseField для данных RGB, где длина каждого из образцов равна 8 бит, будет иметь 3*256 входов. 256 красных входов идут первыми, за ними следуют 256 зеленых входов, за ними следуют 256 синих входов. Поле ColorResponseField должно функционировать как таблица, преобразуя значения образцов в определенные значения интенсивности таким образом, что- бы изображения, созданные в одной системе, могли быть выведены на экран с минимальными потерями точности цвета. Поле ColorResponseField, следователь- но, описывает «цветовую гамму» изображения, чтобы читатель TIFF, работаю- щий в другой системе, смог компенсировать как гамму изображения, так и гамму считывающей системы. Гамма — это термин, который относится к нелинейной компоненте больших устройств отображения, включающих в себя мониторы. В большинстве систем отображения напряжение, приложенное к ЭЛТ, прямо пропорционально значе- ниям красного, зеленого и синего образцов. Однако результирующая яркость,
Дополнение Н. Колориметрическое описание изображения 495 выделяемая фосфором, не пропорциональна напряжению. Это соотношение в большинстве дисплеев выражается приблизительно следующим равенством: Яркость = Напряжение ** Гамма Гамма в стандарте NTSC равна 2,2, она описана в большинстве видеосистем. Значение гаммы, равное 2,2, означает тусклую среду. (Мы знаем, что гамма не ре- комендуется в стандарте SMPTE.) В следующем примере использован 8-битовый образец со значением 127. Напряжение = 127/255 = 0,4980 Яркость = 0,4980 ** 2,2 = 0,2157 В примерах, описанных ниже, мы берем в расчет только единственное главное значение и поэтому только единственную компоненту. То же самое применяется к каждому из значений компонент красного, зеленого и синего цвета. Также мы предполагаем, что таблицы цвета отсутствуют и мы должны сами изменять зна- чения элементов изображения. Если имеется таблица цвета, можно выполнять проводить действия в таблице вместо того, чтобы проводить их на элементах изображения. Если в цветном изображении не существует поля ColorResponseCurves, чита- тель может принять значение гаммы, равное 2,2, для каждой из компонент. Это значение по умолчанию может быть создано с помощью следующего кода Си: ValuesPerSample = 1 << BitsPerSample; for(curve[0] = 0, i = 1; i < ValuesPerSample; i ++) curve[i] = floor (pow (i/(ValuesPerSample — 1.0), 2.2) * 65535.0+ .5); Выведенная на экран или воспроизведенная программа знает свое собствен- ное значение гаммы, которое мы будем называть «гамма назначения» (destination gamma). (В некалиброванной системе значение гаммы обычно принимается рав- ным 2,2.) Используя эту информацию, программа может компенсировать значе- ние гаммы для изображения, что мы и увидим ниже. Если во входной и в выходной системах гамма равна 2,2, автор может опу- стить поле ColorResponseCurves и читатель может считать изображение прямо в кадр буфера. Если автор записывает поле ColorResponseCurves, читатель дол- жен допустить, что значения гаммы различаются. Читатель должен затем осу- ществить следующие вычисления каждого образца в изображении: NewSampleValue = floor (pow (curve[OldSampleValue]/65535.0,1.0/ DestinationGamma) * (ValuesPerSample — 1.0) + .5); Конечно, если гамма выходной системы не аппроксимируется хорошо экспо- ненциальной функцией, можно использовать произвольную таблицу преобразо- ваний в точке увеличения значения до 1.О/DestinationGamma. Пропустим поле ColorResponseCurves, если используется значение гаммы по умолчанию. Это сэкономит около 1,5 К в самом общем случае, и, кроме того, про- пуск данного поля выгоден для сжатия. Не используйте это поле для хранения цветовых таблиц в кадре буфера. Вместо этого пользуйтесь полем ColorMap. Однако отметим, что поле ColorResponseCurves при желании может быть ис- пользовано для очистки информации в ColorMap. В приведенных выше примерах принимается, что параметр гамма адекватно приравнивается к характеристикам
496 Приложение 2 исходного изображения и выходной системы. Обычно это верно, но в нашем слу- чае использования таблицы вместо параметра гамма дает возможность гибкого описания более сложных взаимоотношений без дополнительных вычислений или сложностей. 8. Новые теги и другие изменения Перечисленные ниже теги должны быть помещены в части «Основные поля» спецификации TIFF: WhitePoint Tag = 318(13Е) Type = RATIONAL N =2 Белая точка изображения. Отметим, что это значение используется при при- менении хроматической диаграммы CIExyY 1931 и определена только хроматич- ность. Компонента яркости является произвольной и не определена. Это соответ- ствует белой точке монитора, где было нарисовано изображение в комбинации установки фильтра/светового источника сканера, или белой точке модели осве- щения воспроизводящей кассеты. Значение по умолчанию — белая точка SMPTE, D65: х = 0,313, у = 0,329. Порядок: х, у. Р rimary Chromaticities Tag = 319(13F) Type = RATIONAL N =6 Хроматичность главных цветов. Отметим, что эти значения описывают, ис- пользуя хроматическую диаграмму CIExyY 1931, и определяют только хрома- тичность. Для прорисовки изображений они представляют хроматичность мони- тора, а для сканированных изображений они получаются из комбинации уста- новки фильтра/светового источника сканера. Значение по умолчанию — хроматичность основных цветов SMPTE: Красный: х = 0,635 у = 0,340 Зеленый: х = 0,305 у = 0,595 Синий: х = 0,155 у = 0,070 Порядок следующий: красный х, красный у, зеленый х, зеленый у, синий х, синий у. Color Response Curves Значение по умолчанию — компоненты, соответствующие гамме, равной 2,2, в стандарте NTSC. 9. Значения по умолчанию Значения по умолчанию, которые использует формат TIFF, отражают промы- шленные стандарты. Как тег WhitePoint, так и тег PrimaryChromaticities имеют
Дополнение Н. Колориметрическое описание изображения 497 значения по умолчанию, которые поддерживаются SMPTE. Кроме того, зна- чение по умолчанию тега ColorResponseCurves соответствует значению гаммы, равное 2,2 в спецификации NTSC. Эти значения по умолчанию позволяют получать приемлемые результаты в отсутствие точных колориметрических данных. Некалиброванные сканер или система прорисовки производят изображение, которое воспроизводится тожде- ственно, хотя, возможно, и неверно на экранах двух различных, но калиброван- ных систем. Это лучше, чем неопределенная ситуация, когда изображение может быть воспроизведено по-разному в двух различных, но калиброванных системах. 10. Ограничения и результаты Ниже описаны некоторые ограничения и результаты, включенные в колориме- трическую репродукцию. Бесполезный объем Для многих целей рекомендованные здесь данные не нужны и могут быть опуще- ны. При презентации графики, где имеется только несколько цветов, способность различения красного от зеленого цвета достаточно хорошая. В этом случае теги могут не учитываться. В средах, которым требуются большие цветовые репро- дукции, эти данные позволяют описывать изображения независимо от устройств и недорого. Заботы пользователя Рекомендуемые нами данные не являются заботой пользователя, это реальный выпуск системы. Он предлагает системное решение, но не требует при этом уча- стия пользователя. Однако калибровка является отдельным выпуском и, веро- ятно, требует участия пользователя. Разрешение как функция точности воспроизведения Некоторые производители поддерживают разрешение, большее 24 бит, для цве- товой спецификации с целью устранения обмана зрения (такого, как контуриро- вание в тенях) либо в некоторых случаях для большей специфичности или не- зависимости устройства от цвета. Обе причины могут быть неверными. Другие, менее дорогие методы можно использовать для предотвращения обмана зрения, например такого, как глубокое цветовое отображение. Что касается точности, то четкость воспроизведения более важна, чем точность. Колориметрическое цветовое воспроизведение В работе [9] описаны другие выборы для целей цветового воспроизведения. Спек- тральное цветовое воспроизведение — наиболее сильное требование, остальные требования — более слабые, например, предпочитаемое цветовое воспроизведе- ние. В то время как спектральное цветовое воспроизведение на независимом устройстве невозможно, колориметрическое воспроизведение на независимом устройстве в пределах допустимого диапазона и ограничений устройств возмож- но. В поисках сильного критерия мы включим и слабые критерии, такие, как предпочитаемое цветовое воспроизведение, в пакеты разработки. 32 3
498 Приложение 2 Метамерия Если два цветных участка идентичны при одном свете и различаются при дру- гом, то, говорят, что они составляют метаметрическую пару. Колориметрическое цветовое воспроизведение является более слабым усло- вием, чем спектральное цветовое воспроизведение и, следовательно, допускает метамерию. С помощью стандартизации условий мы в большей степени сможем решить проблемы метамерии при распечатке. Так как телевидение является са- мосветящимся и не использует спектральное поглощение, метамерия не предста- вляет в этом случае слишком больших трудностей. Модели цвета — xyY в зависимости от Luv и так далее Мы выбрали зависимость xyY от Luv, так как XYZ является международным стандартом для спецификации цвета и xyY является диаграммой хроматичности, связанной с XYZ. Luv — это способ измерения разности цветов. Окружающая среда и зрительное окружение Зрительное окружение — это то, как глаз воспринимает цвет. Глаз адаптируется к темной комнате и к цветовому окружению. В то время как эти проблемы мо- гут быть разрешены в пределах колориметрической структуры [4], гораздо лучше устранить их с помощью стандартизации. Среда должна соответствовать предна- значенному зрительному окружению. В среднем это должен быть нейтральный цвет. Для распечатки стандарт ANSI рекомендует поверхность Munsell N-8 [8]. 11. Библиография Мы хотим особенно отметить работу Стюарта Ринга из Copy Products Division of Eastman Kodak Company. Он и его коллеги способствовали взаимообмену образ- цов цветовых данных. Они тесно сотрудничают с ISO 8613 Work Group [7]. [1] Color Data Interchange Paradigm, Eastman Kodak, Rochester, New York, 7 December 1987. [2] Color Repoduction and Illumination Models, Roy Hall, International Summer Institute: State of the Art in Computer Graphics, 1986. [3] CIE Colorimetry: Official Recommendations of International Commission on Illumi- nation, Publication 15-2, 1986. [4] Color Science: Concepts and Methods, Quantitative Data and Formulae, Gunter Wyszecki, W. S. Stiles, John Wiley and Sons, Inc., New York,1982. [5] Color Monitor Colorimetry, SMPTE Recommended Practice RP 145-1987. [6] Color Temperature for Color Television Studio Monitors, SMPTE Recommended Prac- tice RP 37. [7] Office Document Architecture (ODA) and Interchange Format — Addendum on Colour, ISO 8613 Working Draft. [8] ANSI Standart PH 2.30-1985. [9] The Reproduction of Colour in Photography, Printing and Television, R. W. G. Hunt, Fountain Press, Tolworth, England, 1987. [10] Raster Graphics Handbook, The Conrac Corporation, Van Nostrand Reinhold Com- pany, New York, New York, 1985.
Приложение 3 Преобразование программ в другие Си-компиляторы Введение Ниже мы рассмотрим преобразование программ, представленных в книге, в Си- компилятор, отличный от Turbo С. Мы допускаем, что преобразование осу- ществляется в Microsoft С (благодаря широкому распространению), хотя этот же процесс можно применить при преобразовании в любой Си-компилятор. Преобразование программы на Turbo С в другой ANSI-компилятор (например, Microsoft С) выполняется достаточно просто. Преобразование компилятора Си в стандарт, отличный от ANSI, потребует больше усилий. Поскольку для преобра- зования программы на Turbo С в программу на Microsoft С требуются небольшие изменения и они приведены ниже, обычно такое преобразование длится прибли- зительно 30 мин. В промышленных применениях обычно используется термин «проход» в программном обеспечении процесса преобразования. Так как и Turbo С, и Microsoft С являются компиляторами стандарта ANSI, все особенности языка Си остаются идентичными. Языковые различия имеют место в том случае, когда используются возможности вне спецификации — гра- фические расширения и определенные расширения размещения памяти. К сча- стью, программы в этой книге не используют расширений стандарта, отличного от ANSI, языка Си. Где бы ни использовались расширения в Turbo С, они долж- ны быть тщательно перенесены в среду нового компилятора. Как Turbo С так и Microsoft С являются хорошими компиляторами, в ко- торых совершенствуются программы обработки изображения. Оба компилято- ра обладают достоинствами и недостатками. Turbo С со своей полной средой и богатыми графическими способностями делает программирование легким и приятным. Программа на Turbo С, описанная в книге, меньше по размеру, чем программа на Microsoft С. Хотя Microsoft С и менее удобен в использовании, он создает программу, которая работает быстрее, чем программа на Turbo С. Мы бы с радостью обменяли малую скорость выполнения на простоту раз- работки программы. Это мнение авторов. У вас по этому поводу может быть свое личное мнение. Возможно, вы бы выполнили вашу программу, которая вас больше всего устраивает. Как уже говорилось выше, между программами на Turbo С и Microsoft С, при- веденными в данной книге, существуют различия. Причина большинства разли- чий заключается в использовании условной компиляции в исходном файле про- граммы. На самом деле она не применялась, так как это усложняет программу. В тех нескольких случаях, когда программы на Turbo С и Microsoft С существенно различаются (как вы сможете увидеть), условная компиляция все усложнит. Как говорилось в ч. I, цель библиотеки функций VGA состоит в поддержке режима 13Н VGA (разрешение 320 х 200, 256 цветов), так как Turbo С не осу- ществляет такой поддержки. Однако Microsoft С осуществляет поддержку для этого режима VGA. При переносе программы в Microsoft С у вас есть выбор — 32*
500 Приложение 3 использовать функции в библиотеке функций VGA или функции, предусмотрен- ные Microsoft С. Microsoft С не требует для инициирования перед исполнением устройств ви- деографики, как это требуется для Turbo С. По этой причине вызов функции InitGraph в программе на Turbo С может быть полностью исключен при переносе программы в Microsoft С. Развитию программы на Microsoft С помогает программа make. Она делает процесс создания (сборка, компиляция, компоновка) программного обеспечения автоматическим, как и программа создания проекта в Turbo С. Программа make (программа project make) проверяет данные всех файлов, которые составляют полное программное обеспечение. Бели компонент файла модернизирован, про- грамма make производит правильное согласование команд ассемблера, компиля- тора и/или компоновщика для программы выполнения. Изучите документацию Turbo С или Microsoft С для полного понимания функционирования программы make. Здесь для наших целей достаточно осознать, что файлы создания проекта для каждого примера программы, представленные в книге, должны быть пре- образованы в файлы «таке» для большинства других программных обеспечений. Чтобы показать сходство и различия между файлами «project make» и «таке», как это требуется в Microsoft С, в качестве примера используется «Программа изображения с различным разрешением» в гл. 5. Ниже показано содержание файла «allvideo.prj» (это файл «project make» для программы в гл. 5): graphics.lib vgagraph.obj digitize.obj egavga.obj pcx (misc.h pcx.h) vga (misc.h pcx.h vga.h) allvideo (misc.h pcx.h vga.h digitize.h) Файл «make», требуемый Microsoft С, показан ниже в том же примере про- граммы. Он будет называться «allvideo.mak». CSwitch я /с /Zpil /G2 /Ох LnSwitch » /СО /МАР pcx.obj : рсх.с misc.h pcx.h cl $(CSwitch) рсх.с vga.obj : vga.c misc.h pcx.h vga.h cl $(CSwitch) vga.c allvideo.obj : allvideo.c misc.h pcx.h vga.h cl $(CSwitch) allvideo.c vgagraph.obj : vgacraph.asm masm vgagraph.asm,,, digitize.obj : digitize.asm masm digitize.asm,,,
Процесс преобразования 501 allvideo.exe : allvideo.obj рсх.obj vga.obj vgagraph.obj digitize.obj link $(LnkSwitch) allvideo+pcx+vga+vgagraph+digitize,\ allvideo,allvideo,graphics.lib+slibce.lib; Программа «таке» запускается из командной строки DOS командой make all video .так. Процесс преобразования Преобразование программы на Turbo С в различные компиляторы Си может вы- полняться в три стадии, перечисленные ниже. Затраты, прилагаемые при этом, возрастают на каждой следующей стадии. Например, усилия, требуемые на пер- вой стадии, достаточно тривиальны, в то время как на третьей стадии требуется гораздо больше усилий. Вы можете попробовать выполнить все три стадии пре- образования программы на Turbo С для работы с вашим компилятором. 1. Преобразование признаков языка Си: это признаки, которые определя- ют, какому стандарту (K&R или ANSI) соответствует ваш компилятор (также называемый выходным компилятором). В этом случае требуют- ся небольшие усилия, до тех пор пока ваш компилятор следует тому же стандарту, что и Turbo С. Если ваш компилятор другого стандарта, вы должны проделать некоторую работу. Вы должны связать входные фай- лы для снятия всех проверок типа параметра вызванной функции и снять указатели на пустоты. Используйте все предостережения, полученные ва- шим компилятором, для проведения этого преобразования. По возможно- сти выполняйте необходимые изменения во входном коде для устранения предостережений. 2. Для преобразований многих нестандартных расширений (например, гра- фическое расширение), используемых Turbo С, в расширения, необходи- мые входному компилятору, иногда используются прямые заменяемые операторы. Этот тип преобразования также требует немного усилий, так как определено замещение языка один к одному. 3. Перезапись программы требуется, когда функциональность, представлен- ная компилятором Turbo С (и сопровождающей его библиотекой), не име- ет эквивалента во входном компиляторе, для которого и преобразуется программа. Продолжая примеры преобразования программы Turbo С в Microsoft С, мы обнаруживаем, что на первой стадии не требуется никаких усилий, так как оба компилятора ANSI-стандарта и поэтому они являют- ся совместимыми. Как и ожидалось, расширения языка, предложенные этими компиляторами, различны. Эти различия должны учитываться в преобразованиях на второй и третьей стадиях, описанных ниже. Прямые заменяемые операторы Прямые замещения — это те пункты (функции, параметры, включенные файлы и другие) в среде Turbo, которые имеют соответствие с эквивалентными пункта- ми в выходной среде. Другими словами, такая же функциональность существует в выходной среде, возможно, с различными названиями или параметрами, прохо- дящими преобразования. Эти типы изменений должны отвечать за выполнение
502 Приложение 3 условной компиляции. Это основные пункты, которые не контролируются ни стандартом ANSI, ни стандартом K&R и поэтому отличаются от компилятора к компилятору. При перенесении в среду Microsoft С замените программу, опре- деленную для Turbo С, на программу, определенную для Microsoft С (MSC), с помощью следующих шагов. 1. Выбор видеорежима VGA Turbo С: setgraphmodeО; MSC: set videomode О; Параметры, подходящи^ к этим функциям, для выбора различных графиче- ских режимов также должны быть изменены. Ниже показано соответствие: Графические режимы Параметры Turbo С Параметры Microsoft С 320 х 200 256 цветов Не поддерживается -MRES256COLOR 640 х 200 16 цветов VGALO -HRES16COLOR 640 х 350 16 цветов VGAMED -ERESCOLOR 640 х 480 16 цветов VGAHI -VRES16COLOR Например, выбор графического режима 640 х 480 Turbo С: setgraphmode(VGAHI); MSC: setvideomode(.VRES16C0L0R); 2. Перевод VGA из графического и текстовый режим Turbo С: restorectmode(); MSC: setvideomode(.DEFEULTMODE); 3. Установка элемента изображения на графическом экране Turbo С: putpixel(х,у,color); MSC: .setcolor(color); .setpixel(x,y); 4. Чтение элемента изображение с экрана Turbo С: getpixel(x,y); MSC: .getpixel(x,у); 5. Очистка текстового экрана Turbo С: drscrO; MSC: .clearscreen(.GCLEARSCREEN); 6. Остановка выполнения программы в определенный период времени Turbo С: delay(); MSC: нет эквивалента 7. Определение количества свободного пространства в дальней памяти Turbo С: farcoreleftО; MSC: нет эквивалента 8. Выделение блока в дальней памяти
Перезапись кода 503 Turbo С: farcallocO; MSC: hallocO; Параметры обеих функций одинаковы. 9. Освобождение олока, выделенного в дальней памяти Turbo С: far free О; MSC: hfreeO; Параметры обеих функций одинаковы. 10. Включение файлов Turbo С: "graphics.h" MSC: "graph.h" Turbo C: "alloc.h" MSC: "inalloc, h" Перезапись кода Последней стадией в программном обеспечении процесса преобразования явля- ется установка функциональности, пригодной в Turbo С, которая была непри- годна для выходного компилятора. Это приращение функциональности должно быть записано в преобразованную программу на Си для того, чтобы программы успешно выполнялись. В преобразовании программ на Turbo С в программы на Microsoft С мы обнаружили, что только функции манипуляции палитрой отсут- ствуют в Microsoft С. По этой причине часть программы, с помощью которой обрабатываются палитры, должна быть переписана для Microsoft С следующим образом: Программа на Turbo С для чтения и сохранения выведенной на экран в настоящее время палитры. Этот пример извлечен из файла "рсх.с", где происходит в основном обработка палитры. /♦ Чтение VGA-палитры в структуру данных палитры. */ getpalette(ftpalette); /♦ Для каждого входа в структуре данных палитры находят значение RGB-компоненты цветового регистра, связанного с входом палитры. Эти значения хранятся в части палитры структуры файла PCX данных. ♦/ for(lndex«0; Index<palette.size; Index++) { regs.h.ah * 0x10; /«Получить функциональный код цветового регистра компонент RGB ♦/ regs.h.al я 0x15; ColorRegisterNun ж palette.colors[Index]; reg.x.bx » ColorRegisterNun; int86(VIDEO,ftregs,ftregs); /♦ Значения RGB должны быть отмасштабированы с коэффициентом 4 до помещения в файл. ♦/ PCXData.Palette[Index].Red я regs.h.dh <<я 2; PCXData. Palette [Index] .Green я regs. h. ch «я 2; PCXDat a. Palette [Index] .Blue я regs.h.cl <<« 2;
504 Приложение 3 } Программа на Microsoft С для чтения и сохранения выведенной на экран в настоящее время палитры. for(lndex»0; Index<MAXPALETTECOLOR; Index++) regs.h.ah ж 0x10; /* Получить функциональный код входа регистра палитры. ♦/ regs.h.al я 0x07; regs.h.bl s Index; /♦ номер индекса для входа */ int86(VIDEO»ftregs,ftregs); ColorRegisterNum » reg.h.Ы; /*ноиер цветового регистра в bl reg */ regs.h.ah ж 0x10; /* Получить функциональный код цветового регистра компонент RGB */ regs.h.al s 0x15; reg.x.bx s ColorRegisterNum; int86(VIDEO»ftregs»ftregs); /♦Значения RGB должны быть отиасштабированы с коэффициентом 4 до помещения в файл. ♦/ PCXDat a. Palette [Index] .Red ж regs.h.dh <<s 2; PCXData. Palette [Index] .Green * regs.h.ch «» 2; PCXData.Palette[Index].Blue я regs.h.cl «я 2; Программа на Turbo С для создания и установки VGA-палитры. Эта программа извлечена из файла "vga.c". /♦ Загрузка серой палитры. ♦/ void LoadGrayl6Palette(void) struct palettetype palette; unsigned Index; union REGS regs; /♦ С установкой графического режима мы можем продолжать загружать нашу палитру и цветовые регистры в DAC. Палитра устанавливается в последовательном порядке и регистры цвета устанавливаются в значения полутоновых цветов. ♦/ palette.size я MAXPALETTECOLORS; /♦ 16 цветов ♦/ /♦ Палитра прямо устанавливает соответствие. Нулевой вход палитры является нулевым цветовым регистром» вход палитры 1 - регистр цвета 1 и так далее. ♦/ for(lndex»0; Index<MAXPALETTECOLORS; Index++) palette.color[Index] я Index; /♦ Установка блока цветовых регистров. ♦/ regs.h.ah я 0x10;
Перезапись кода 505 regs.h.al ж 0x12; regs.x.bx ж 0; regs.x.cx ж MAXPALETTECOLORS; _ES » FP_SEG(Grayl6ColorPalette); regs.x.dx ж FP_0FF(Grayl6ColorPalette); int86(VIDEO,ftregs,Aregs); /♦ Установка вновь созданной: палитры. ♦/ setallpalette(ftpalette); Программа на Microsoft С для создания и установки палитры VGA. /♦Загрузка серой палитры.♦/ void LoadGrayl6Palette(void) union REGS regs; struct SREGS segregs; /* С установкой графического режима мы можем продолжать загружать нашу палитру и цветовые регистры в DAC. Палитра устанавливается в последовательном порядке и регистр цвета устанавливается в полутоновые значения. ♦/ /♦ Палитра прямо устанавливает соответствие. Нулевой вход палитры является нулевым цветовым регистром» вход палитры 1 - регистр цвета 1 и так далее. ♦/ for(lndex»0; Index<MAXPALETTECOLOR; Index++) { regs.h.ah ж 0x10; regs.h.al ж 0x00; /♦ Установка функционального кода регистра палитры ♦/ regs.h.bh ж Index; /♦ Номер цветового регистра ♦/ regs.h.bl ж Index; /♦ Номер входа палитры ♦/ int86(VIDEO,ftregs,ftregs) ; /♦ Установка входа палитры в номер цветового регистра ♦/ } segread(ftsegregs); /♦ Чтение значений регистров сегмента ♦/ /♦Установка блока цветовых регистров ♦/ regs.h.ah = 0x10; regs.h.al ж 0x12; regs.x.bx ж 0; regs.x.cx ж MAXPALETTECOLORS; /♦ Установка всех 16 входов ♦/ segregs.es ж segregs.ds; /♦ es:dx pts в массиве палитры ♦/ regs.x.dx ж Gray64ColorPalette; int86(VIDEO,ftregs,ftregs); } Далее следует согласование всех трех только что перечисленных стадий для окончательного процесса преобразования.
506 Приложение 3 1. Построение файла «таке» с использованием в качестве файла «project таке», который использует Turbo С. 2. Связывание входных файлов Си для включения изменений, требуемых в Си-программе. 3. Выполнение программы «таке» в файле «таке», построенном на 1-й ста- дии, для компиляции новой рабочей программы. Обратите внимание на ошибки или предостережения, выданные компилятором и компоновщи- ком. Все ошибки и предостережения должны быть устранены до того, как ваше преобразование будет выполнено. 4. Если получаются ошибки или предостережения, вернитесь на стадию 2 и исправьте их. 5. Если нет ошибок и предостережений, запускайте программу. 6. Если программа правильно не выполняется, возвращайтесь на стадию 2 и исправьте ошибки. Когда вы достигнете этой стадии, процесс преобразования будет завершен. Хотя программа на Microsoft С использовалась как адресат для процесса пре- образования, показанного здесь, такие же стадии могут быть использованы для преобразования программы, написанной на Turbo С и представленной в данной книге, в любой другой компилятор. Далее следует содержимое файла misc.h: /ale**************************#**##********/ /♦ Header файл ♦/ /♦ Вспомогательные определения ♦/ /♦ разработан в Turbo С 2.0 ♦/ /♦ Крэйгом А. Линдли ♦/ /♦ ♦/ /♦ Версия: 1.0 ♦/ /♦ Последние изменения внесены 10/05/89 ♦/ /«с****#******#****#*********#************/ /* Определяем новый тип BYTE ♦/ «ifndef .BYTE «define .BYTE typedef unsigned char BYTE; «endif «define TRUE 1 «define FALSE 0 «define VIDEO 0x10 /♦ Некоторые общеупотребительные макрокоманды */ «define MIN(a,b) ((a)>(b)) ? (b):(a) «define MAX(a,b) ((a)>(b)) ? (a):(b) «define SQUARE(x) (x)*(x)
Предметный указатель Адаптер дисплея 14 - VGA 14 Алгоритм определения краев по Собелю 350, 366 - усредненного фильтрования 342 - сжатия LZW 478 - срединного сечения 176 - Собеля 369 - свертки 342 Алгоритмы обработки изображения 294 Анализирующий код командной строки 148 Аналоговая часть видеопреобразователя 70 Аппаратные счетчики элементов изобра- жения 76 Аппроксимация к ближайшему соседу 411, 412 Арифметическая операция add 381 Архитектура сегментной организации па- мяти 12 Ассемблер 28 Базовые поля 449 Белая точка 194 Библиотека функций адаптера VGA 40 - - геометрических процессов 398, 416 - - покадрового процесса 395 - - пространственного процесса 343 - - точечного процесса 310, 327 - - PCX 149, 187, 216, 221, 226 - - TIFF 228, 240, 241, 244 Буфер данных изображения PictData 134 - изображения 135, 149, 179 Векторные изображения 186 Векторный формат 185, 186 Вертикальная зеркальность изображения 430 Весовой фактор см. Коэффициент сверт- ки 358 Видеокабель 137 Видеопереключатель 128 Видеопреобразователи 65 Видеостандарт NTSC 56 Видеостандарты 60 Вращение изображения 421, 427 Время восстановления 85 Выбор карты цветов 175 Вывод изображения на дисплей 176 Выравнивающие синхроимпульсы 57, 117 - фильтры 368 Высокочастотные пространственные филь- тры 363 Выходные файлы PCX 148, 149 Вычитание изображений 383 Геометрические преобразования 397 - процессы 294, 397 Гистограмма 321 Глобальная динамическая область памя- ти 45 Горизонтальная зеркальность изображе- ния 430 Граничные эффекты 419 Графический адаптер 12 Групповые процессы 341 Данные изображения 296 < Действительные» флаговые биты 468 Декодирование по методу LZW 481 Дискретизация изображения 175 Дробный адрес элемента изображения 411 Заголовок файла изображения 445 - - РСХ/РСС 210 Задающий генератор 81 Защелка счетчика элементов 76, 77, 118, 122 Зеркальные изображения 430 Инициирование таблицы преобразования 325 Интенсивность элемента изображения 286 Интервал гашения 55 Интерполяция 411 Интерфейс компьютер/видеопреобразова- тель 68 Информационные поля (TIFF) 459 Исходная палитра 214 Кадр 375 Кадровое гашение 55, 56 Кадровые синхроимпульсы 57, 87, 117 Каталог файла изображения 447 Категории контраста 334 Квантовые изображения 176 Классы TIFF 238, 485 Ключ командной строки 148
508 Предметный указатель Ключи командной строки программы cvideo 175 Код интерфейса пользователя 284, 290 - - печати экрана 284 - - принтера 284, 285 - расчета интенсивности 284, 286 Кодовое слово 469 Колориметрическое описание 492 Колориметрические стандарты 492 Команда NOP 88 Комбинирующие функции 379 Композиционные видеосигналы 57 Компоненты цвета 494 Контраст изображения 333 Коррекция разрешения дисплея 422 - соотношения геометрических разме- ров 422, 427 Коэффициент свертки см. Весовой фак- тор 358 Линейная интерполяция 412 Линия High/Low 78 - Synk Reset 78 Макрокоманда LDELAY 88, 126 - SDelay 85 - StbDel 86 Масштабирование изображения 411, 424 Матрица псевдотонирования 267 - Риландера 266, 267 Матрицирование 262, 264 Матрицированные изображения 290 Матрицы элементов изображения 265 Медианная фильтрация см. Усредненное фильтрование 342 Метод псевдотонирования 15 - Флойда-Стейнберга 150, 162, 175 - глобальных переменных 49 - передачи параметров 49 Методика псевдоцветов 49 Методы подгонки пары палитра/изобра- жение 21 - уплотнения данных 230 - упорядочения псевдотонирования 267 Механизм палитры 214 Модель регистров для процессора 8086/88 13 Монохромные ЭЛТ 54 Негативные изображения 330 Иеквадратные элементы изображения 420, 427 Неполадки, связанные с накруткой 81 Низкочастотные пространственные филь- тры 362 Область примыкания 341, 412, 419 Обработка изображения 294 Обратное отображение 411, 412 - преобразование 425 Окно фокуса 137 Описатель цвета 215 Оптические денситометры 452 Отрицательная логика 26 Параллельный порт принтера 23 Палитра 17 - файлов PCX 213 Параметры ImageReq ПО Первичные цвета 55 Перечень TIFF-тегов 474 Позиционирование изображения 56 Позиционный формат 468 Полоса 243, 457 - данных изображения 235 Поле упорядочения битов 230 Покадровые процессы 294, 375 Положительная логика 26 Полу кадр 55 Полутонирование 262 Полутоновые изображения 15, 66 Получение печатных копий изображения на принтере 262 Пороговое отсекание 262, 263, 332 Пороговые изображения 332 Приватные поля 466 - теги 233 Программа автовывода 181 - вывода изображения общего назначе- ния 130 - - оцифрованного изображения с пол- ным набором цветов 130, 149 - геометричевких процессов 398 - - функций 406 - окна фокуса 130, 136 - печати экрана 269 - покадровых процессов 385, 389 - пространственных процессов 342, 352 - точечного процесса 317 - цветной оцифровки cvideo.exe 175 - View 186, 256 Прозрачная маска 455 Просветление 327 Пространственная разрешающая способ- ность 295 - частота 295, 341 Пространственное объединение изображе- ния 262 Пространственные процессы 294, 341 - фильтры 341 Процедура Delay 113
Предметный указатель 509 - Get Picture 112 - InitializeDigitizer 114, 135, 136 Процессор 8086 10 Псевдокод для RLL-сжатия 218 Псевдораскрашивание 337 Псевдотонирование 164, 262, 267, 463 - по методу Флойда-Стейнберга 175 Псевдотонированные изображения 267, 290, 463 РСС-изображения 213 РСС-файлы 213 Размытие изображения 368 Разрешение видеопреобразователя 65 - видеосистемы 60 - дисплея 420 Распаковка данных: 481 Растровые данные 231 - изображения 216, 185, 477 Растровый формат 185 Расширенная палитра 215 Реальная палитра 214 Резкость изображения 363 RLL-сжатие 217 Сборка видеопреобразователя 79 Свертка 342, 358 Сдвиг 427 Сжатие данных: 217 - - в формате TIFF 235 - - по методу ограничения длины прохо- да (RLL) 212, 217 - - - - LZW 452, 477 ----PackBits 472 Сигнал цветности 61 Синхронизация видеоимпульсов 87 - видеокадра 58 - видеостроки 59 - электронного луча 56 Система глаз/мозг 174 Системы с отрицательной синхронизаци- ей 57 Скольжение гистограмм 327 Смещение значения 135 Совместимость по коду 10 Согласованные классы TIFF 238 Соответствие один к одному 411 Соотношение геометрических размеров 271, 419 Спецификация формата TIFF 5.0 443 Способы распределения данных 48, 49 Стандарт RS-170 59 - NTSC 60 - PAL 62 - SECAM 62 Строчное гашение 55 Строчный синхроимпульс 57, 88 Структура данных гистограмм 323 - - ImageReq 88 - оцифровывающих программ 129 - Req 135 - тега 468 - TIFF 445 - файлов в формате TIFF 230 Схема кодирования 469 - факсимильного сжатия CCITT Group 3 469 - SyncEOC 78 Счетчик повторений 218 Таблица квантования 176 - преобразований 381 - DensityTbl 286 Таблицы преобразований 309, 324 Тег 231 - «Compression» 235, 237 Тестирование видеопреобразователя 81, 122 Тест SyncsPerField 136 Тип логики 26 Типы данных 233 - тега 233 Точечные процессы 294, 309 Требования реального времени 84 TIFF В-класс для двухуровневых изобра- жений 488 Turbo С 29 Увеличение контраста 322 Указатели типа HUGE 46, 110, 134, 227, 304 - - FAR 47 - - NEAR 114 Упорядочение битов 230 Упорядоченная матрица псевдотонирова- ния Байера 267 Уровень шума видеокамеры 137 Усиление края 363, 366 - - методом сдвига и разности 367 --направленного градиента 368 - - по Лапласу 366 Усредненное фильтрование 342 см. Меди- анная фильтрация 342 Усредняющая функция Combinelmage 385 Устаревшие поля 463 Файл автовывода 180 - allvideo.c 141 - cvideo.prj 141 - cvideo.c 150
510 Предметный указатель - cvideo.prj 150 - cvideo.exe 32 - digitize.li 89 - digitize.asm 90 - gvideo.с 131 - gvideo.prj 131 - misc.h 30, 506 - PCX 180 - pcx.h 188 - pcx.c 190 - pvideo.c 138 - pvideo.prj 138 - testl.prj 122 - test2.prj 124 Файлы библиотеки функций TIFF 240 - изображений с теговой организацией 231 - необработанных данных изображения 228 Фактор горизонтального масштабирова- ния 416 - вертикального масштабирования 416 Формат графических файлов TIFF 186, 216, 228, 444 - - - РСХ/РСС 186 Фотографическое копирование изображе- ний 261 Функции драйвера низкого уровня 84 - гистограмм 323 - поддержки низкого уровня 112 - - обработки изображений 296, 304 - пространственной обработки 356 Функциональность графического адапте- ра 15 Функция автовывода 181 - вращения изображения Rotateimage 418 - вычитания Sub 383 - зеркального изображения Mirrorimage 419 - масштабирования изображения Scale- Image 416 - определения краев по Собелю 357 - поддержки Initialize Timer2 113 - поразрядного умножения And 379 - размера изображения 417 - свертки действительных чисел 357 - - целых чисел 356 - сдвига изображения Translatelmage 418 - Adjimage Brightness 329 - Combinelmage 376 - Compress Scanline 218 - Display PictDatal 147 - - PictData2 147 - Dither PrintCol 289 - Expand Scaneline 221 - FindLong Sync 117 - GetColorReg 43 - GetPixelFromlmage 303 - GetPicture 118 - GetPixel25b 41 - GetVideoMode 42 - InitGraphics 41, 135 - InitializeDigitizer 149 - InitializeLUT 325 - LoadGrayl6Palette 43 - main 135 - Negatelmage 330 - Or 381 - Overlay 385 - Prtchar 285 - Prtlnit 285 - PrtStatus 285 - PrtString 285 - PrtScreen 287 - PutPixel256 40, 131 - PutPixellnlmage 303 - ReadPCXTileToBuf 227 - SetAColorReg 43 - SetPixelCount 118 - Set256ColorMode 42 - ShowHelp 147 - StretchlmageContrast 337 - SyncsPerField 116 - Threshold Im age 332 - TIFFClose 243 - PtTransform 326 - WritePCXFile 226 - Xor 381 Цветовая насыщенность 177 Цветные изображения 15 - светофильтры 177 - ЭЛТ 54 Цикл while 137 Цифровая часть видеопреобразователя 74 ЦПУ 10 Чересстрочная видеосистема в стандарте NTSC 55 Число циклов процессора 86 ' Шаблон 368 Ядро свертки 358 - размытия 360 Яркостное разрешение 195
Оглавление Предисловие.................................................. 5 Благодарности................................................ 8 Часть I. Проект цифрового видеопреобразователя 9 Глава 1. Общие сведения...................................... 9 Глава 2. Основы видеотехники................................ 54 Глава 3. Аппаратные средства цифрового видеопреобразователя. 63 Глава 4. Программные средства видеопреобразователя низкого уровня для ввода изображения................,.................. 83 Глава 5. Программные средства видеопреобразователя высокого уровня для управления процессом вывода изображения на дисплей. 127 Глава 6. Форматы и функции графических файлов.............. 185 Глава 7. Создание печатных копий изображений............... 261 Часть II. Классические методы обработки изображения 292 Глава 8. Функции поддержки обработки изображения .......... 296 Глава 9. Точечные процессы................................. 309 Глава 10. Пространственные процессы........................ 341 Глава 11. Покадровые процессы.............................. 375 Глава 12. Геометрические процессы.......................... 397 Часть III. Дополнительные сведения 431 Толковый словарь терминов.................................. 431 Литература................................................. 436 Приложение 1. Перечень функций PC BIOS..................... 438 Приложение 2. Графический формат TIFF. Спецификация 5.0.... 443 Приложение 3. Преобразование программ в другие Си-компиляторы .. 499 Предметный указатель....................................... 507
Научное издание Крэйг Линдли ПРАКТИЧЕСКАЯ ОБРАБОТКА ИЗОБРАЖЕНИЙ НА ЯЗЫКЕ СИ Заведующий редакцией Т. Г. Хохлова Ведущий редактор И. М. Андреева Художник С. А. Бычков Художественный редактор О. Н. Адаскина Технический редактор Е. В. Денюкова Корректор Т. М. Подгорная Оригинал-макет подготовлен С. А. Янковой в пакете ЮТрХ с использованием кириллических шрифтов, разработанных в редакции АИП издательства «Мир» ИБ № 8389 Лицензия Л.Р. №010174 от 22.01.92 г. Подписано к печати 27.03.96 г. Формат 70 х 1001/16. Бумага офсетная. Печать офсетная. Объем 16,00 бум. л. Усл.-печ. л. 41,60. Усл. кр.-отт. 41,60. Уч.-изд. л. 41,29. Изд. №6/9249. Тираж 5 000 экз. Заказ З.С094 Издательство «Мир» Комитета Российской Федерации по печати. 129820, ГСП, Москва, И-110, 1-й Рижский пер., 2. Можайский полиграфкомбинат Комитета Российской Федерации по печати. 143200, Можайск, ул. Мира, 93.