Пишем векторизатор изображений

  С помощью векторизатора изображений можно перевести растровую картинку в векторную форму. Векторизатор выделяет основные контуры по определенному алгоритму, поэтому результат его работы будет всегда хуже чем при ручной трассировке. Однако, его применение может быть оправдано в том случае, когда необходимо трассировать большое количество изображений, либо когда картинка имеет резкие контуры и содержит мало полутоновых переходов. Свой результат программа сохраняет в файлы ILDA.
  Здесь мы рассмотрим каким образом можно написать такую программу и на что следует обратить внимание. При разработке своей утилиты я использовал наиболее простые алгоритмы.

  В общем случае, процесс векторизации включает в себя несколько этапов: фильтрация изображения, сама векторизация и подчистка.

1. Фильтрация.
  Фильтрация изображения необходима для выявления контуров, с которыми потом будет работать трассировщик. При этом, на выходе будет сформировано два изображения: черно-белое(двоичное) изображение - карта движения трассировщика и цветное изображение по которому трассировщик будет окрашивать векторы в нужный цвет. Все генерируемые контуры на выходе имеют толщину 1 пиксел - условие для качественной работы векторизатора.
  Каждый пиксел изображения представлен тремя цветовыми компонентами. Резкая смена значения компоненты скорее всего будет свидетельствовать о наличие контура. При этом необходимо выставлять нижний порог яркости для срабатывания, т.к. при малой яркости имеется повышенный уровень шума(это наиболее хорошо заметно на изображениях в формате JPG). В целом, можно сказать, что здесь происходит высокочастотная фильтрация сигналов яркости. Я попробовал нескольцо алгоримов: БПФ и простое вычитание соседних пикселей. Результат простого вычитания мне больше понравился, его я и использовал.
  Изображение можно сканировать слева-направо. Т.е. в пределах одной строки вычитается предыдущий пиксел из следующего. При этом мы заметим, что линии, идущие параллельно строкам исчезнут т.к. здесь они представлены низкочастотными компонентами.

Похожий эффект происходит и при сканировании сверху-вниз.

Теперь с помощью операции ИЛИ объединим две матрицы изображений и получим то, что нам надо.

Т.о. в своей программе я использую двойное сканирование.

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

2. Векторизация.
  В качестве трассировщика я выбрал рекурсивный 8-связный алгоритм заливки с затравкой, при этом доработав его под свою задачу.

  По очереди перебираются все пикселы двоичного изображения-карты. Как только будет обнаружен какой-нибудь пиксел контура(белый пиксел), то он является затравкой и с этого места запускается трассировщик. Трассировщик проверяет 8 соседних пикселей и если хотя бы один из них допускает движение, то вызывается очередной экземпляр функции трассировщика. При этом в стек копируется координата и цвет следующего пиксела по которому произошел переход. А текущий пиксел из карты движения перекрашивается в черный цвет - так мы избегаем повторной трассировки одних и тех же пикселей. Во избежание переполнения стека в функцию добавлен счетчик вызовов.
  Если при трассировке мы дошли до последнего пиксела линии и соседние 8 точек не допускают движения, то линия закрывается точкой бланкинга(пустой точкой) и продолжается процесс поиска новой затравки(т.е. поиск новой белой точки). Этот этап продолжается до тех пор, пока в процессе перебора мы не дойдем до последней точки изображения.

Упрощенно функция трассировщика выглядит так:

Sub Wave(ByRef ImageMatrix() As ImgType, ByVal X As Integer, ByVal Y As Integer)
    If StackCounter > 2000 Then Exit Sub

    StackCounter = StackCounter + 1

    With VStack(StackCounter)
        .X = X
        .Y = Y
        .R = ImageMatrix(X, Y).Red
        .G = ImageMatrix(X, Y).Green
        .B = ImageMatrix(X, Y).Blue
    End With

    If ImageMatrix(X + 1, Y).Status = 1 Then  'Right
        Call Wave(ImageMatrix, X + 1, Y)
    End If
    If ImageMatrix(X, Y + 1).Status = 1 Then  'down
        Call Wave(ImageMatrix, X, Y + 1)
    End If
    If ImageMatrix(X - 1, Y).Status = 1 Then  'left
        Call Wave(ImageMatrix, X - 1, Y)
    End If
    If ImageMatrix(X, Y - 1).Status = 1 Then  'up
        Call Wave(ImageMatrix, X, Y - 1)
    End If
    If ImageMatrix(X + 1, Y + 1).Status = 1 Then  'right-down
        Call Wave(ImageMatrix, X + 1, Y + 1)
    End If
    If ImageMatrix(X - 1, Y + 1).Status = 1 Then  'left-down
        Call Wave(ImageMatrix, X - 1, Y + 1)
    End If
    If ImageMatrix(X - 1, Y - 1).Status = 1 Then  'left-up
        Call Wave(ImageMatrix, X - 1, Y - 1)
    End If
    If ImageMatrix(X + 1, Y - 1).Status = 1 Then 'right-up
        Call Wave(ImageMatrix, X + 1, Y - 1)
    End If
End Sub

3. Подчистка.
  При работе трассировщика генерируются точки(или векторы) находящиеся на расстоянии одного пиксела друг от друга. Такая большая плотность точек не позволяет выводить изображения на лазерный проектор. Поэтому необходимо уменьшить количество точек.
  Наиболее просто это сделать на вертикальных и горизонтальных прямых т.к. одна из координат всегда будет постоянной.
Также не составляет труда вырезать точки, лежащие посередине двух других с единичным шагом. Другие случаи являются более проблемными потому что нужно распознавать прямую линию и проверять с определенным допуском, подходят ли точки под нее или нет. Этот метод здесь не был реализован.
  С большой вероятностью можно сказать, что линии, содержащие в себе малое количество точек(2-5) являются шумом. Поэтому их смело можно удалять без ущерба для качества изображения.

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

В результате экспериментов получилась вот такая программка:


 

Скачать исполняемые файлы программы.
Эта программа является 32-разрядным приложением. В архив включены только исполняемые файлы. Если при запуске выдаст сообщение, что не хватает какого-нибудь файла типа COMDLG32.OCX и т.п., то вам следует запустить инсталлятор какой-нибудь программы, написанной на Visual Basic 6. Либо установить сам Visual Basic.

На главную

Hosted by uCoz