Сегментирование символов номерного знака
Я столкнулся с проблемой сегментирования символов по изображению номерного знака. Я применил следующий метод для извлечения символов номерного знака "
- Адаптивный порог изображения номерного знака.
- Выберите контуры, которые имеют определенное соотношение сторон.
Если на изображении номерного знака есть какой-либо оттенок, как в прикрепленном файле, я не могу правильно сегментировать символы из-за неправильной бинаризации. Тень на изображении объединяет соседние символы на изображении.
Я установил пороги для изображений с разными размерами окон. Результаты прилагаются. Как я могу сегментировать символы из изображения, если на изображении есть тень? Я использую OpenCV.
Я использовал следующую функцию в 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
), чтобы получить пороговые изображения.
2 ответа
Прежде чем я начну, я знаю, что вы ищете реализацию этого алгоритма в OpenCV C++, но мой алгоритм требует БПФ и numpy / scipy
пакеты для этого потрясающие. Поэтому я дам вам реализацию алгоритма в OpenCV, используя вместо этого Python. Код на самом деле очень похож на C++ API, который вы можете легко переписать. Таким образом, это минимизирует количество времени, которое потребуется мне для изучения (или, скорее, переучивания) API, и я бы лучше дал вам алгоритм и шаги, которые я сделал, чтобы выполнить эту задачу, чтобы не тратить впустую время вообще,
Таким образом, я дам вам общий обзор того, что я буду делать. Затем я покажу вам код Python, который использует numpy, scipy
и пакеты OpenCV. В качестве бонуса для тех, кто использует MATLAB, я покажу вам эквивалент MATLAB с кодом MATLAB для загрузки!
Что вы можете сделать, это попробовать использовать гомоморфную фильтрацию. В основных терминах мы можем представить изображение в виде продукта освещения и отражательной способности. Предполагается, что освещение медленно меняется и является основным источником динамического диапазона. Это по сути низкочастотный контент. Отражательная способность представляет детали объектов и предполагается, что они быстро изменяются. Это также является основным фактором, влияющим на локальный контраст, и по существу является высокочастотным контентом.
Изображение может быть представлено как произведение этих двух. Гомоморфная фильтрация пробует и разделяет эти компоненты, и мы фильтруем их индивидуально. Затем мы объединяем результаты вместе, когда мы закончим. Поскольку это мультипликативная модель, обычно используется операция регистрации, чтобы мы могли выразить продукт как сумму двух слагаемых. Эти два термина отфильтровываются по отдельности, масштабируются, чтобы подчеркнуть или уменьшить акцент их вклада в изображение, суммируются, затем берется антилог.
Затенение происходит из-за освещения, и поэтому мы можем уменьшить вклад, который это затенение вносит в изображение. Мы также можем повысить коэффициент отражения, чтобы получить более качественные границы, так как края связаны с высокочастотной информацией.
Мы обычно фильтруем освещение, используя фильтр нижних частот, а коэффициент отражения - фильтром верхних частот. В этом случае я выберу ядро Гаусса с сигмой 10 в качестве фильтра нижних частот. Фильтр верхних частот можно получить, взяв 1
и вычитание с помощью фильтра нижних частот. Я преобразую изображение в лог-область, затем фильтрую изображение в частотной области, используя фильтры нижних и верхних частот. Затем я масштабирую результаты низких и высоких частот, добавляю эти компоненты обратно, а затем беру анти-лог. Это изображение теперь лучше подходит для установки по порогу, так как изображение имеет небольшие вариации.
То, что я делаю в качестве дополнительной постобработки, - это пороговое изображение Буквы темнее общего фона, поэтому любые пиксели ниже определенного порога будут классифицироваться как текст. Я выбрал порог для интенсивности 65. После этого я также удаляю все пиксели, которые касаются границы, а затем удаляю все области изображения, которые имеют менее 160 (MATLAB) или 120 (Python) пикселей общей площади. Я также обрезаю некоторые столбцы изображения, поскольку они не нужны для нашего анализа.
Вот пара предостережений для вас:
Предостережение № 1 - Удаление границ
Удаление любых пикселей, которые касаются границы, не встроено в OpenCV. Тем не менее, MATLAB имеет эквивалент под названием imclearborder
, Я буду использовать это в своем коде MATLAB, но для OpenCV это был следующий алгоритм:
- Найти все контуры на изображении
- Для каждого контура на изображении проверьте, не находятся ли пиксели контура в пределах границы изображения.
- Если есть, отметьте этот контур для удаления
- Для каждого контура, который мы хотим удалить, просто нарисуйте весь этот контур черным
Я создал метод под названием imclearborder(imgBW, radius)
в моем коде, где radius
сколько пикселей в пределах границы вы хотите очистить вещи.
Предостережение № 2 - удаление областей пикселей ниже определенной области
Удаление любых областей, где их меньше определенной суммы, также не реализовано в OpenCV. В MATLAB это удобно давать, используя bwareaopen
, Основной алгоритм для этого:
- Найти все контуры на изображении
- Проанализируйте, насколько площадь каждого контура заполняется, если вы должны были заполнить интерьер
- Любые области, которые меньше определенного количества, очищают этот контур, заполняя интерьер черным
Я создал метод под названием bwareaopen(imgBW)
это делает это для нас.
Предупреждение № 3 - параметр Area для удаления областей пикселей
Для кода Python мне пришлось поиграться с этим параметром, и я остановился на 120. 160 использовался для MATLAB. Для питона 120 избавился от некоторых персонажей, что нежелательно. Я предполагаю, что моя реализация bwareaopen
по сравнению с MATLAB отличается, поэтому, вероятно, я получаю разные результаты.
Без лишних слов, вот код. Обратите внимание, что я не использовал пространственную фильтрацию. Вы могли бы использовать filter2D
в OpenCV и свертить это изображение с ядром Гаусса, но я не сделал этого, так как гомоморфная фильтрация при использовании низкочастотных и высокочастотных фильтров традиционно выполняется в частотной области. Вы можете исследовать это, используя пространственную фильтрацию, но вам также придется заранее знать размер ваших ядер. При фильтрации в частотной области вам просто нужно знать стандартное отклонение фильтра, и это всего лишь один параметр по сравнению с двумя.
Кроме того, для кода Python я загрузил ваше изображение на свой компьютер и запустил скрипт. Для 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's 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('https://stackru.com/images/0385e8ffee3ddc32487b733f095eee706d4e37d9.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's 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');
Вот результат, который я получаю:
питон
Обратите внимание, что я переставил окна так, чтобы они были выровнены в одном столбце.
MATLAB
Я думаю, вы получите хорошее изображение, если примените морфологическую операцию открытия ко второму предоставленному вами двоичному изображению.