Сегментирование символов абонентской платы

Я столкнулся с проблемой сегментирования символов с изображения номерного знака. Я применил следующий метод для извлечения символов номерного знака "

  • Адаптивный порог изображения номерного знака.
  • Выберите контуры, имеющие конкретное соотношение сторон.

Если на изображении номерной карточки есть какой-либо оттенок, как в прикрепленном файле, я не могу правильно сегментировать символы из-за неправильной бинаризации. Тень на изображении объединяет соседние символы в изображении.

Я установил изображения с разными размерами окна. Результаты прилагаются. Как можно отделять символы от изображения, если на изображении есть оттенок? Я использую OpenCV.

License plate image with shade

threshoded image 1

thresholded image 2

thresholded image 3

Я использовал следующую функцию в OpenCV для порога моего изображения номерного знака:

cvAdaptiveThreshold(licensePlateImg, threshImg, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, wind);

Я пробовал с разными размерами окна (wind) и разными adaptiveMethod (ADAPTIVE_THRESH_MEAN_C and ADAPTIVE_THRESH_GAUSSIAN_C) для получения пороговых изображений.

Ответ 1

Прежде чем я начну, я знаю, что вы ищете реализацию этого алгоритма в OpenCV С++, но для моего алгоритма требуется FFT, а пакеты numpy / scipy для этого потрясающие. Таким образом, я дам вам реализацию алгоритма в OpenCV вместо Python. Код на самом деле очень похож на С++ API, который вы можете легко переписать вместо этого. Таким образом, это сводит к минимуму количество времени, которое потребуется мне для изучения (или, вернее, переучивания...) API, и я бы скорее дал вам алгоритм и шаги, которые я сделал для выполнения этой задачи, чтобы не тратить время вообще,

Таким образом, я дам вам общий обзор того, что я буду делать. Затем я покажу вам код Python, который использует numpy, scipy и пакеты OpenCV. В качестве бонуса для тех, кто использует MATLAB, я покажу вам эквивалент MATLAB, с кодом MATLAB для загрузки!


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

Изображение может быть представлено как продукт этих двух. Гомоморфная фильтрация пытается и разбивает эти компоненты, и мы их фильтруем отдельно. Затем мы объединяем результаты вместе, когда мы закончим. Поскольку это мультипликативная модель, принято использовать операцию log, чтобы мы могли выразить произведение в виде суммы двух слагаемых. Эти два члена фильтруются индивидуально, масштабируются, чтобы подчеркнуть или де-подчеркивать их вклад в изображение, суммировать, затем берется анти-журнал.

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

Мы обычно фильтруем освещение с использованием фильтра нижних частот, а коэффициент отражения - фильтром верхних частот. В этом случае я собираюсь выбрать гауссовское ядро ​​с сигмой 10 в качестве фильтра нижних частот. Высокочастотный фильтр можно получить, взяв 1 и вычитая с помощью фильтра нижних частот. Я преобразую изображение в домен журнала, затем фильтрую изображение в частотной области с использованием фильтров нижних частот и верхних частот. Затем я масштабирую результаты низких и высоких частот, добавляю эти компоненты назад, а затем беру анти-журнал. Это изображение теперь лучше подходит для порога, так как изображение имеет низкую вариацию.

Что я делаю в качестве дополнительной пост-обработки, так это то, что я порою изображение. Буквы темнее общего фона, поэтому любые пиксели, которые ниже определенного порога, будут классифицированы как текст. Я выбрал порог для интенсивности 65. После этого я также удаляю любые пиксели, которые касаются границы, а затем удаляет любые области изображения с менее чем 160 (MATLAB) или 120 (Python) пикселей общей площади. Я также вырезаю некоторые из столбцов изображения, поскольку они не нужны для нашего анализа.


Вот несколько предостережений для вас:

Предостережение № 1 - Удаление границ

Удаление любых пикселей, которые касаются границы, не, встроенных в OpenCV. Однако MATLAB имеет эквивалент, называемый imclearborder. Я буду использовать это в своем коде MATLAB, но для OpenCV это был следующий алгоритм:

  • Найти все контуры изображения
  • Для каждого контура, который находится на изображении, проверьте, находится ли какой-либо из контурных пикселей в пределах границы изображения.
  • Если есть, отметьте этот контур для удаления
  • Для каждого контура, который мы хотим удалить, просто нарисуем весь контур в черном

Я создал метод под названием imclearborder(imgBW, radius) в моем коде, где radius - сколько пикселей внутри рамки, которую вы хотите очистить.

Предостережение # 2 - Удаление областей пикселей ниже определенной области

Удаление любых областей, где они меньше определенной суммы, также является не, реализованным в OpenCV. В MATLAB это удобно использовать с помощью bwareaopen. Основным алгоритмом для этого является:

  • Найти все контуры изображения
  • Проанализируйте, насколько каждая область контура заполняется, если вы должны заполнить интерьер
  • Любые области, которые меньше определенной величины, очищают этот контур, заполняя интерьер черным цветом

Я создал метод под названием bwareaopen(imgBW), который делает это для нас.

Caveat # 3 - параметр области для удаления областей пикселей

Для кода Python мне пришлось поиграть с этим параметром, и я установил 120. 160 был использован для MATLAB. Для python 120 избавился от некоторых символов, что нежелательно. Я предполагаю, что моя реализация bwareaopen по сравнению с MATLAB отличается, поэтому я, вероятно, получаю разные результаты.


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

Кроме того, для кода Python я загрузил ваше изображение на свой компьютер и запустил script. Для MATLAB вы можете напрямую ссылаться на гиперссылку на изображение при чтении с помощью панели инструментов Image Processing.


Код Python

import cv2 # For OpenCV modules (For Image I/O and Contour Finding)
import numpy as np # For general purpose array manipulation
import scipy.fftpack # For FFT2 

#### imclearborder definition

def imclearborder(imgBW, radius):

    # Given a black and white image, first find all of its contours
    imgBWcopy = imgBW.copy()
    contours,hierarchy = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, 
        cv2.CHAIN_APPROX_SIMPLE)

    # Get dimensions of image
    imgRows = imgBW.shape[0]
    imgCols = imgBW.shape[1]    

    contourList = [] # ID list of contours that touch the border

    # For each contour...
    for idx in np.arange(len(contours)):
        # Get the i'th contour
        cnt = contours[idx]

        # Look at each point in the contour
        for pt in cnt:
            rowCnt = pt[0][1]
            colCnt = pt[0][0]

            # If this is within the radius of the border
            # this contour goes bye bye!
            check1 = (rowCnt >= 0 and rowCnt < radius) or (rowCnt >= imgRows-1-radius and rowCnt < imgRows)
            check2 = (colCnt >= 0 and colCnt < radius) or (colCnt >= imgCols-1-radius and colCnt < imgCols)

            if check1 or check2:
                contourList.append(idx)
                break

    for idx in contourList:
        cv2.drawContours(imgBWcopy, contours, idx, (0,0,0), -1)

    return imgBWcopy

#### bwareaopen definition
def bwareaopen(imgBW, areaPixels):
    # Given a black and white image, first find all of its contours
    imgBWcopy = imgBW.copy()
    contours,hierarchy = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, 
        cv2.CHAIN_APPROX_SIMPLE)

    # For each contour, determine its total occupying area
    for idx in np.arange(len(contours)):
        area = cv2.contourArea(contours[idx])
        if (area >= 0 and area <= areaPixels):
            cv2.drawContours(imgBWcopy, contours, idx, (0,0,0), -1)

    return imgBWcopy

#### Main program

# Read in image
img = cv2.imread('5DnwY.jpg', 0)

# Number of rows and columns
rows = img.shape[0]
cols = img.shape[1]

# Remove some columns from the beginning and end
img = img[:, 59:cols-20]

# Number of rows and columns
rows = img.shape[0]
cols = img.shape[1]

# Convert image to 0 to 1, then do log(1 + I)
imgLog = np.log1p(np.array(img, dtype="float") / 255)

# Create Gaussian mask of sigma = 10
M = 2*rows + 1
N = 2*cols + 1
sigma = 10
(X,Y) = np.meshgrid(np.linspace(0,N-1,N), np.linspace(0,M-1,M))
centerX = np.ceil(N/2)
centerY = np.ceil(M/2)
gaussianNumerator = (X - centerX)**2 + (Y - centerY)**2

# Low pass and high pass filters
Hlow = np.exp(-gaussianNumerator / (2*sigma*sigma))
Hhigh = 1 - Hlow

# Move origin of filters so that it at the top left corner to
# match with the input image
HlowShift = scipy.fftpack.ifftshift(Hlow.copy())
HhighShift = scipy.fftpack.ifftshift(Hhigh.copy())

# Filter the image and crop
If = scipy.fftpack.fft2(imgLog.copy(), (M,N))
Ioutlow = scipy.real(scipy.fftpack.ifft2(If.copy() * HlowShift, (M,N)))
Iouthigh = scipy.real(scipy.fftpack.ifft2(If.copy() * HhighShift, (M,N)))

# Set scaling factors and add
gamma1 = 0.3
gamma2 = 1.5
Iout = gamma1*Ioutlow[0:rows,0:cols] + gamma2*Iouthigh[0:rows,0:cols]

# Anti-log then rescale to [0,1]
Ihmf = np.expm1(Iout)
Ihmf = (Ihmf - np.min(Ihmf)) / (np.max(Ihmf) - np.min(Ihmf))
Ihmf2 = np.array(255*Ihmf, dtype="uint8")

# Threshold the image - Anything below intensity 65 gets set to white
Ithresh = Ihmf2 < 65
Ithresh = 255*Ithresh.astype("uint8")

# Clear off the border.  Choose a border radius of 5 pixels
Iclear = imclearborder(Ithresh, 5)

# Eliminate regions that have areas below 120 pixels
Iopen = bwareaopen(Iclear, 120)

# Show all images
cv2.imshow('Original Image', img)
cv2.imshow('Homomorphic Filtered Result', Ihmf2)
cv2.imshow('Thresholded Result', Ithresh)
cv2.imshow('Opened Result', Iopen)
cv2.waitKey(0)
cv2.destroyAllWindows()

Код MATLAB

clear all;
close all;

% Read in image
I = imread('http://i.stack.imgur.com/5DnwY.jpg');

% Remove some columns from the beginning and end
I = I(:,60:end-20);

% Cast to double and do log.  We add with 1 to avoid log(0) error.
I = im2double(I);
I = log(1 + I);

% Create Gaussian mask in frequency domain
% We must specify our mask to be twice the size of the image to avoid
% aliasing.
M = 2*size(I,1) + 1;
N = 2*size(I,2) + 1;
sigma = 10;
[X, Y] = meshgrid(1:N,1:M);
centerX = ceil(N/2);
centerY = ceil(M/2);
gaussianNumerator = (X - centerX).^2 + (Y - centerY).^2;

% Low pass and high pass filters
Hlow = exp(-gaussianNumerator./(2*sigma.^2));
Hhigh = 1 - Hlow;

% Move origin of filters so that it at the top left corner to match with
% input image
Hlow = ifftshift(Hlow);
Hhigh = ifftshift(Hhigh);

% Filter the image, and crop
If = fft2(I, M, N);
Ioutlow = real(ifft2(Hlow .* If));
Iouthigh = real(ifft2(Hhigh .* If));

% Set scaling factors then add
gamma1 = 0.3;
gamma2 = 1.5;
Iout = gamma1*Ioutlow(1:size(I,1),1:size(I,2)) + ...
       gamma2*Iouthigh(1:size(I,1),1:size(I,2));

% Anti-log then rescale to [0,1]
Ihmf = exp(Iout) - 1;
Ihmf = (Ihmf - min(Ihmf(:))) / (max(Ihmf(:)) - min(Ihmf(:)));

% Threshold the image - Anything below intensity 65 gets set to white
Ithresh = Ihmf < 65/255;

% Remove border pixels
Iclear = imclearborder(Ithresh, 8);

% Eliminate regions that have areas below 160 pixels
Iopen = bwareaopen(Iclear, 160);

% Show all of the results
figure;
subplot(4,1,1);
imshow(I);
title('Original Image');
subplot(4,1,2);
imshow(Ihmf);
title('Homomorphic Filtered Result');
subplot(4,1,3);
imshow(Ithresh);
title('Thresholded Result');
subplot(4,1,4);
imshow(Iopen);
title('Opened Result');

В результате я получаю:

Python

Обратите внимание, что я повторно разместил окна так, чтобы они были выровнены в одном столбце.

enter image description here

MATLAB

enter image description here

Ответ 2

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

Ответ 3

Какой алгоритм вы использовали в этом коде Python?